[Zinc] SyncQueue

For discussions regarding skinning and mapmaking for Warcraft 3.

Moderator: Moderators

[Zinc] SyncQueue

Postby StealthOfKing » Mon Oct 12, 2009 9:21 pm

SyncQueue

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();
        }
    }
}
StealthOfKing
 
Posts: 2
Joined: Thu Feb 14, 2008 2:07 pm

Re: [Zinc] SyncQueue

Postby Cassiel » Mon Oct 12, 2009 9:46 pm

Well, I certainly have no problem with anyone posting scripts here, but if you want people to actually use this I imagine a high-traffic place like THW would be better.

I also don't really recommend using RtC, but it's your map.
User avatar
Cassiel
 
Posts: 5087
Joined: Wed May 31, 2006 2:12 pm

Re: [Zinc] SyncQueue

Postby StealthOfKing » Tue Oct 13, 2009 1:13 am

I was testing the waters with the concept of this idea, perhaps with the intention of asking you to support the program with your map. I don't care too much for my work getting traffic.

The program is not RtC! RtC requires a new common.j (or atleast it did when I was looking into it a long time ago), and is not the sort of thing we are developing. This is passive functions inside any normal BNet map, there is no obligation to run the program but if you do you will receive enhanced gameplay.

Choosing to support this would be like choosing to support HCL, either you do or you don't but its not going to do any harm if you do.
StealthOfKing
 
Posts: 2
Joined: Thu Feb 14, 2008 2:07 pm


Return to Custom Mapmaking

Who is online

Users browsing this forum: No registered users and 2 guests

cron