SyncQueue is a robust and reliable way of syncing data from one player to another. Syncing is 100% accurate with this, it has an event driven system to inform your map when a value has been synced. Strings don't sync at all. This will be available in vJass as part of Fate Engine.
This had to be made for use in conjunction an external program that allows map makers to create their own DLL injected natives. Anyone running the program with Warcraft III can use the natives (it injects like a map hack), and this library can be used to sync the data. It opens up many doors for things like saving heroes without savecodes in RPGs, gathering player scores or tournament data from a centralised server etc.
Spoiler:
- Code: Select all
library SyncQueue {
/*
SyncQueue
¯¯¯¯¯¯¯¯¯
Finally a fail safe way of syncing data between players! Thanks to SquallLeonhart for ideas and research into the subject
of gamecache syncing.
Struct API
sync.boolean(fromPlayer, identifier, value, onSyncFunc);
sync.integer(fromPlayer, identifier, value, onSyncFunc);
sync.real (fromPlayer, identifier, value, onSyncFunc);
OnSyncFunc
¯¯¯¯¯¯¯¯¯¯
An important part of this sync system is letting the players know when something has been successfully synced. Every sync
can be linked to a function via three different function interfaces. That function is executed when the sync is succesful,
and tells all players what the synced value is.
OnSyncFunc API
type onsyncbooleanfunc extends function(player, string, boolean);
type onsyncintegerfunc extends function(player, string, integer);
type onsyncrealfunc extends function(player, string, real);
*/
// Globals
gamecache cache; // Does all the syncing.
syncqueue syncQueue; // Holds values currently being synced.
syncqueue toSyncQueue; // Holds values that will be synced in the next itteration.
integer K = 0; // Generates a unique key for every sync.
boolean isPlaying[];
// Types
public type onsyncbooleanfunc extends function(player, string, boolean); constant integer BOOLEAN = 0x01;
public type onsyncintegerfunc extends function(player, string, integer); constant integer INTEGER = 0x02;
public type onsyncrealfunc extends function(player, string, real); constant integer REAL = 0x03;
// Structs
public struct syncqueue {
sync first = 0;
sync last;
integer length = 0;
method operator isEmpty() -> boolean {
return (this.length == 0);
}
method add(sync t) {
if (this.first == 0) {
this.first = t;
} else {
this.last.next = t;
}
this.last = t;
t.next = 0;
this.length = this.length + 1;
}
method remove() -> sync {
sync t = this.first;
if (this.isEmpty) {
return 0;
}
this.first = t.next;
this.length = this.length - 1;
return t;
}
}
public struct sync {
boolean bData;
integer iData;
real rData;
sync next;
integer p;
string id;
string key;
integer func;
integer returnType;
static method boolean(player fromPlayer, string identifier, boolean value, integer func) {
sync s = sync.create();
s.bData = value;
s.p = GetPlayerId(fromPlayer);
s.id = identifier;
s.func = func;
s.returnType = BOOLEAN;
s.key = identifier+"_"+I2S(K);
toSyncQueue.add(s);
K = K + 1;
}
static method integer(player fromPlayer, string identifier, integer value, integer func) {
sync s = sync.create();
s.iData = value;
s.p = GetPlayerId(fromPlayer);
s.id = identifier;
s.func = func;
s.returnType = INTEGER;
s.key = identifier+"_"+I2S(K);
toSyncQueue.add(s);
K = K + 1;
}
static method real(player fromPlayer, string identifier, real value, integer func) {
sync s = sync.create();
s.rData = value;
s.p = GetPlayerId(fromPlayer);
s.id = identifier;
s.func = func;
s.returnType = REAL;
s.key = identifier+"_"+I2S(K);
toSyncQueue.add(s);
K = K + 1;
}
}
function onLeave() {
isPlaying[GetPlayerId(GetTriggerPlayer())] = false;
}
function onUpdate() {
sync s = syncQueue.first;
boolean isSynced = false;
integer i, p = GetPlayerId(GetLocalPlayer());
string key = I2S(K);
for (0 <= i <= 11) {
StoreInteger(cache, key, "sync"+I2S(i), -1);
}
// Sync data.
TriggerSyncStart();
StoreInteger(cache, key, "sync"+I2S(p), 1);
while (s != 0) {
if (s.p == p) {
if (s.returnType == BOOLEAN) { StoreBoolean(cache, key, s.key, s.bData); SyncStoredBoolean(cache, key, s.key); }
else if (s.returnType == INTEGER) { StoreInteger(cache, key, s.key, s.iData); SyncStoredInteger(cache, key, s.key); }
else if (s.returnType == REAL) { StoreReal (cache, key, s.key, s.rData); SyncStoredReal (cache, key, s.key); }
} else {
if (s.returnType == BOOLEAN) { StoreBoolean(cache, key, s.key, false); }
else if (s.returnType == INTEGER) { StoreInteger(cache, key, s.key, 0); }
else if (s.returnType == REAL) { StoreReal (cache, key, s.key, 0.0); }
}
s = s.next;
}
SyncStoredInteger(cache, key, "sync"+I2S(p));
StoreInteger(cache, key, "sync"+I2S(p), -1);
TriggerSyncReady();
while (!isSynced) {
isSynced = true;
for (0 <= i <= 11) {
if (i:isPlaying) {
if (GetStoredInteger(cache, key, "sync"+I2S(i)) != 1) {
isSynced = false;
}
}
}
if (!isSynced) {
TriggerSleepAction(0.0);
}
}
s = syncQueue.remove();
while (s != 0) {
if (s.returnType == BOOLEAN) { onsyncbooleanfunc(s.func).execute(Player(s.p), s.id, GetStoredBoolean(cache, key, s.key)); }
else if (s.returnType == INTEGER) { onsyncintegerfunc(s.func).execute(Player(s.p), s.id, GetStoredInteger(cache, key, s.key)); }
else if (s.returnType == REAL) { onsyncrealfunc(s.func).execute(Player(s.p), s.id, GetStoredReal(cache, key, s.key)); }
s.destroy();
s = syncQueue.remove();
}
FlushStoredMission(cache, key);
}
function onInitDelayed() {
DestroyTrigger(GetTriggeringTrigger());
}
function onInit() {
integer i;
trigger t = CreateTrigger();
TriggerRegisterTimerEvent(t, 5, false);
TriggerAddAction(t, function onInitDelayed);
cache = InitGameCache("SyncQueue.cache");
StoreInteger(cache, "mission", "key", 1);
syncQueue = syncqueue.create();
toSyncQueue = syncqueue.create();
t = CreateTrigger();
for(0 <= i <= 11) {
i:isPlaying = (GetPlayerController(Player(i)) == MAP_CONTROL_USER) &&
(GetPlayerSlotState(Player(i)) == PLAYER_SLOT_STATE_PLAYING);
TriggerRegisterPlayerEvent(t, Player(i), EVENT_PLAYER_LEAVE);
}
TriggerAddAction(t, function onLeave);
while (true) {
while (!toSyncQueue.isEmpty) {
syncQueue.add(toSyncQueue.remove());
}
onUpdate();
}
}
}