I’ve been trying to look at Circle TD trollforged, and unfortunately i’m fairly certain I just don’t know enough about the coding to actually make any informed descisions.
The only thing i’ve learned browsing hiveworkshop is that there are certain functions that should not be used unless absolutely neccesary, because they are known for causing desyncs (even before reforged) and some of these maps are using them.
The most common one being getlocalplayer()
If there’s a proper way to use getlocalplayer(), i haven’t been able to find an example, only message upon message saying “DO NOT USE”
That all being said, in trollforged’s case, it seems to be used by some custom library called “The Map Meta Data Library”, which I have very little idea as to the purpose of. I think judging from the variables it might be the leaderboard?
//* TMMDL
///////////////////////////////////////////////////////////////
/// The Map Meta Data Library
/// Version: v1.00
/// Last Modified: April 24, 2009
/// Author Chain: Strilanc, [insert next ...]
///////////////////////////////////////////////////////////////
/// This library is used to emit standardized meta data which replay parsers and bot hosts can use to record relevant
/// game statistics like "hero kills" which would otherwise be impossible to record automatically.
///
/// In particular, the flag function can be used to indicate if a leaver should be awarded a win or not. Replays
/// don't contain enough information to easily tell winners who leave from losers who leave. (for example: people
/// who leave while end-game stats are being shown)
///////////////////////////////////////////////////////////////
/// Interface:
/// void FlagPlayer(player, flag_constant)
/// void DefineValue(name, type_constant, goal_constant, suggest_constant)
/// void UpdateValueInt(name, player, operation_constant, value)
/// void UpdateValueReal(name, player, operation_constant, value)
/// void UpdateValueString(name, player, value)
/// void DefineEvent0(name, format)
/// void DefineEvent1(name, format, argName1)
/// void DefineEvent2(name, format, argName1, argName2)
/// void DefineEvent3(name, format, argName1, argName2, argName3)
/// void LogEvent0(name)
/// void LogEvent1(name, arg0)
/// void LogEvent2(name, arg0, arg1)
/// void LogEvent3(name, arg0, arg1, arg2)
/// void LogCustom(unique_identifier, data)
/// void RaiseGuard(reason)
///////////////////////////////////////////////////////////////
/// Notes:
/// - Errors are displayed using BJDebugMsg
/// - Don't try to update a value before defining it
/// - Parsers expect a very specific format, don't screw with the library's output.
/// - If you emit a bunch of data per second, you will cause bandwidth problems for dial-up users. Try to avoid
/// emitting lots of data all at once except at the start and end of games or rounds.
/// - An event's format string uses {#} to represent arguments
/// - Calling RaiseGuard will increase the number of senders for each message from 1 to 3. This increases
/// security but uses more network bandwidth. It is done automatically if tampering is detected.
///////////////////////////////////////////////////////////////
In particular it uses it in:
///Returns true for a fixed size uniform random subset of players in the game
function MMD__isEmitter takes nothing returns boolean
local integer i= 0
local integer n= 0
local integer r
local integer array picks
local boolean array pick_flags
loop
exitwhen i >= 24
if GetPlayerController(Player(i)) == MAP_CONTROL_USER and GetPlayerSlotState(Player(i)) == PLAYER_SLOT_STATE_PLAYING then
if n < MMD__num_senders then //initializing picks
set picks[n]=i
set pick_flags[i]=true
else //maintain the invariant 'P(being picked) = c/n'
set r=GetRandomInt(0, n)
if r < MMD__num_senders then
set pick_flags[picks[r]]=false
set picks[r]=i
set pick_flags[i]=true
endif
endif
set n=n + 1
endif
set i=i + 1
endloop
return pick_flags[GetPlayerId(**GetLocalPlayer()**)]
endfunction
And
///Stores previously sent messages for tamper detection purposes
function s__MMD__QueueNode_create takes integer id,string msg returns integer
local integer this= s__MMD__QueueNode__allocate()
set s__MMD__QueueNode_timeout[this]=(TimerGetElapsed(MMD__clock)) + 7.0 + GetRandomReal(0, 2 + 0.1 * GetPlayerId(**GetLocalPlayer()**)) // INLINED!!
set s__MMD__QueueNode_msg[this]=msg
set s__MMD__QueueNode_checksum[this]=MMD__poor_hash(s__MMD__QueueNode_msg[this] , id)
set s__MMD__QueueNode_key[this]=I2S(id)
return this
endfunction
function s__MMD__QueueNode_onDestroy takes integer this returns nothing
call FlushStoredInteger(MMD__gc, MMD__M_KEY_VAL + s__MMD__QueueNode_key[this], s__MMD__QueueNode_msg[this])
call FlushStoredInteger(MMD__gc, MMD__M_KEY_CHK + s__MMD__QueueNode_key[this], s__MMD__QueueNode_key[this])
set s__MMD__QueueNode_msg[this]=null
set s__MMD__QueueNode_key[this]=null
set s__MMD__QueueNode_next[this]=0
endfunction
Some of it’s functions also say something about “tamper detection purposes”. So basically it’s a fucntion I don’t quite understand the purpose of, using a function that most of the wc3 mapmaking community seems to say should not be used unless absolutely neccesary.
Again I don’t know much about this though, just going on what little I can find. Apparently even Strilac, the guy who made this library, only shows up in a handful of posts on the Hive workshop site.
I also found getlocalplayer being used an extensive amount of times in Warcraft Maul Reimagined (which also has desyncs), but that one uses lua code so I am not sure if it has the same “DO NOT USE” mentality as the JASS version.