Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

Using SharpCraft in Battle.NET with only 1 player having it

Discussion in 'The Lab' started by TriggerHappy, Jul 7, 2016.

  1. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,793
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    I'm making a proper release package for people who want to add optional extensions to their map through SharpCraft.

    Currently the optional feature is HTTP access. I created a demo which will automatically load your character from a web server, and save to it, as long as at least 1 player has SharpCraft in the map.

    You can try the demo attached below.

    Libraries:
    1. HTTP
    2. SharpCraft Extension (JASS)
    3. Sync

    Old Post
    Here's a basic example on how to make SharpCraft an optional feature for your map. This works in Battle.Net and only requires 1 player to have SharpCraft running.

    For demonstration I created a basic HTTP implementation. This lets you do simple GET/POST requests to websites.

    With this you could do things like store and retrieve player data online, but there are much more possibilities.

    Code (vJASS):

    scope HttpExample initializer Init

        globals
            gamecache SharpCraftCache
        endglobals

        function HasSharpCraft takes nothing returns boolean
            return GetStoredBoolean(SharpCraftCache, "SC", "0")
        endfunction

        private function OnReceiveString takes nothing returns boolean
            local SyncData data = GetSyncedData()
     
            call BJDebugMsg(GetPlayerName(data.from) + " downloaded \"" + data.readString(0) + "\"")
     
            call data.destroy()
            return false
        endfunction
     
        private function OnMapStart takes nothing returns nothing
            local string response = ""
            local SyncData data
     
            call StoreString(SharpCraftCache, "ARG", "0", "HTTP")
            call StoreString(SharpCraftCache, "ARG", "1", "http://185.92.220.118/mmo/status.php")
            call StoreString(SharpCraftCache, "ARG", "2", "END")
     
            call Cheat("RunSharpCraftCommand")
     
            call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Downloading website...")
     
            call TriggerSleepAction(2.5)
     
            call Cheat("StoreLastResults HTTP")
     
            set response = GetStoredString(SharpCraftCache, "HTTP", "0")
     
            set data = SyncData.create(Player(0))
            call data.addString(response, 64)
            call data.addEventListener(function OnReceiveString)
            call data.start()
        endfunction

        //===========================================================================
        private function Init takes nothing returns nothing
            local trigger t = CreateTrigger()
            call TriggerRegisterTimerEvent(t, 0.00, false)
            call TriggerAddAction(t, function OnMapStart)
     
            set SharpCraftCache=InitGameCache("sc.w3v")
            call Cheat("SharpCraftInit")
            if (not HasSharpCraft()) then
                call InitGameCache("sc.w3v")
            endif
        endfunction

    endscope
     
     

    Attached Files:

    Last edited: Sep 19, 2016
  2. Where's the socket fiddling?
     
  3. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,793
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    Check the imports in the demo map, there's a plugin there which handles everything.
     
  4. I don't know how to use VS it seems.. where can I get to the code?
    7thJuly16.png
     
  5. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,793
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    Here's the updated code:


    Code (C#):

    using MindWorX.SharpCraft.Modules.JassAPI;
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using TinkerWorX.SharpCraft;
    using System.Net;
    using System.Threading;
    using MindWorX.SharpCraft.Modules.WarAPI;

    namespace TriggerHappy.Modules.HttpTrick
    {
        [Requires(typeof(JassAPIPlugin))]
        unsafe public class HttpTrickPlugin : IMapPlugin
        {
            public static JassGameCache Cache;
            public static MindWorX.SharpCraft.Modules.WarAPI.Types.HT_Node* Global;

            public static Dictionary<int, string> LastResults = new Dictionary<int, string>();

            public HttpTrickPlugin()
            {
            }

            public void Initialize(PluginContext context)
            {
                if (context != PluginContext.Game)
                    return;

                Natives.Add(new CheatPrototype(this.Cheat));
            }

            public void OnMapStart()
            {
                Natives.Add(new CheatPrototype(this.Cheat));

                //Cache = Natives.InitGameCache("sc.w3v");

                //Global = Game.Jass.AsUnsafe()->VirtualMachine[0]->GlobalTable->Lookup("SharpCraftCache");
                //Global->Value = Cache.Handle.ToPointer();
            }

            private delegate void CheatPrototype(JassStringArg cheatStr);
            private void Cheat(JassStringArg cheat)
            {
                try
                {
                    string cmd;
                    string cheatStr = cheat.ToString();
                    bool hasArgs = false;

                    if (cheatStr == "SharpCraftInit")
                    {
                        Cache = Natives.InitGameCache("sc.w3v");

                        Natives.StoreBoolean(Cache, "SC", "0", true);

                        return;
                    }

                    if (cheatStr.Contains(' '))
                    {
                        cmd = cheatStr.Split(' ')[0];
                        hasArgs = true;
                    }
                    else
                    {
                        cmd = cheatStr;
                    }

                    switch (cmd)
                    {
                        case "StoreLastResults":

                            if (!hasArgs)
                                return;

                            cmd = cheatStr.Split(' ')[1].ToUpper();

                            foreach (KeyValuePair<int, string> pair in LastResults)
                            {
                                Natives.StoreString(Cache, cmd, LastResults[pair.Key], pair.Value);
                            }

                            break;

                        case "RunSharpCraftCommand":

                            string sc_cmd = Natives.GetStoredString(Cache, "ARG", "0").ToString().ToLower();

                            string argStr = "";
                            string a;
                            int localPlayerId = Natives.GetPlayerId(JassPlayer.FromLocal());

                            for (int i = 1; i < 32; i++)
                            {
                                a = Natives.GetStoredString(Cache, "ARG", i.ToString());

                                if (a.ToLower() == "end")
                                    break;

                                argStr += a + "\n";
                            }

                            string[] args = argStr.Split('\n');

                            switch (sc_cmd)
                            {
                                case "http":

                                    if (args.Length == 0)
                                        return;

                                    new Thread(delegate ()
                                    {
                                        string response = "";
                                        int resultIndex = 0;
                                        string url = "";
                                        string method = "GET";
                                        string data = "";
                                        int player = -1;

                                        string[] split;
                                        string split_cmd;

                                        foreach (string arg in args)
                                        {
                                            if (arg.Contains('='))
                                            {
                                                split = arg.Split(new char[] { '=' }, 2);
                                                split_cmd = split[0].ToUpper().Trim().Replace(" ", "").Replace("\t", "");

                                                split[1] = split[1].Trim();

                                                if (split_cmd == "URL")
                                                    url = split[1];
                                                else if (split_cmd == "ID")
                                                    int.TryParse(split[1], out resultIndex);
                                                else if (split_cmd == "METHOD")
                                                    method = split[1].Replace(" ", "");
                                                else if (split_cmd == "DATA")
                                                    data = split[1].Replace(" ", "");
                                                else if (split_cmd == "PLAYER")
                                                    int.TryParse(split[1], out player);
                                            }
                                        }

                                        if (player != -1 && localPlayerId != player)
                                            return;

                                        if (url == "")
                                            url = args[0];

                                        if (url.Substring(0, 7) != "http://")
                                            url = "http://" + url;

                                        try
                                        {
                                            if (method == "POST")
                                            {
                                                var wc = new WebClient();
                                                wc.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
                                                response = wc.UploadString(url, data);
                                                wc.Dispose();
                                            }
                                            else
                                            {
                                                response = new WebClient().DownloadString(url);
                                            }
                                        }
                                        catch(Exception error)
                                        {
                                            response = error.ToString();
                                        }

                                        LastResults[resultIndex] = response;

                                    }).Start();

                                    break;
                            }

                            break;


                        default:
                            Natives.Cheat(cheat);

                            break;


                    }
                }
                catch(Exception error)
                {
                    Natives.DisplayTextToPlayer(JassPlayer.FromLocal(), 0, 0, error.ToString());
                }
            }

            public void OnMapEnd()
            {
                //throw new NotImplementedException();
            }
        }
    }
     
     
    Last edited: Sep 15, 2016
  6. Like java but doesn't suck not java! Ah C#.. I tried it once, I love the class - based approach but I had to remove VS back then cuz of lack of HD space.

    Is there any concise tutorial you recommend when coming from C background?
     
  7. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,429
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    Nice! Hashtables are pretty nice for this as well, since you can take a handle argument. But I like your implementation a lot. I could imagine someone making a preprocessor for this to make it really clean and fancy. :)

    It opens up a lot of potential for user accounts and online interaction. I know a lot of people have invested tons of time into replay parsers and metadata for it, but with this it would be quite simple to just send that info through the game. No need for complex commands and junk, just a simple http post, and the fact that it is optional makes it even better.

    And best of all, only one person needs it! I never thought about that fact. Thank goodness wc3 allows us to sync things. Unfortunately it is pretty slow through wc3 syncing, but it is cool nonetheless. I imagine you can have a bot with sharpcraft running and hosting a map, and then send/sync everything from it. :p Would be pretty cool!
     
  8. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,793
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    I couldn't get the code that accesses global variables to not crash so I used game cache

    It's really not slow. It should have no problem loading a save code from a server and syncing it with all players. Even if only one player is syncing 11 separate, average-sized save codes it should still work relatively fast. Syncing entire web pages could be slow though and you can't do any kind of constant communication with the server quickly.

    Yeah it could be good for RPGs or competitive maps.

    It could also notify players of new updates, currently hosted maps, or even live stream messages to players (like implementing THW chat).

    I will probably make a simple demo showing how to store save codes online and retrieve them.
     
    Last edited: Jul 8, 2016
  9. oger-lord

    oger-lord

    Joined:
    Jul 24, 2006
    Messages:
    156
    Resources:
    2
    Maps:
    1
    Tutorials:
    1
    Resources:
    2
    This reminds me of a system that allowed bots to save and load data of a map by providing a Jass API. Mainly used for stats.
    I think it was called w3mmd?
     
  10. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,429
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    Sorry, I didn't mean the http request is slow. I know that is fast since it doesn't have to go through bnet. I just meant wc3's syncing itself, but it shouldn't be a problem as long as you aren't trying to sync tons of things between players.

    Anyway, good luck! :) That demo sounds like a good idea.
     
  11. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,793
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    I know what you meant, but I still mean the wc3 syncing is fast (enough).

    If 1 player has SharpCraft, he's responsible to sync everything to all players, and it should be do-able in under a second with 11 average sized save codes.

    I haven't tested Sync with more than 5 players in b.net though.
     
    Last edited: Jul 9, 2016
  12. Waffle

    Waffle

    Joined:
    Jul 30, 2013
    Messages:
    280
    Resources:
    0
    Resources:
    0
    wouldn't it be possible to accomplish http using preload natives + network file paths and maybe like a proxy server to turn smb into http and back.(speculation, interested but have not had time to tinker with it unfortunately)

    talking about an on-line save/load repository you could even just have a plain old publicly acessible directory on a server. (obviously must be pedantic to not have epic security fails with this approach)

    would then run without any custom war3 executables.

    yeah having codeless save/load + unline repo for codes would be epic :D
     
  13. Xonok

    Xonok

    Joined:
    Mar 27, 2012
    Messages:
    3,039
    Resources:
    8
    Spells:
    3
    Tutorials:
    5
    Resources:
    8
    This could be an interesting. I remember at one point I wanted something like this, but can't remember what for.
     
  14. Zwiebelchen

    Zwiebelchen

    Joined:
    Sep 17, 2009
    Messages:
    7,014
    Resources:
    12
    Models:
    5
    Maps:
    1
    Spells:
    1
    Tutorials:
    1
    JASS:
    4
    Resources:
    12
    So this would work if only a hostbot has sharpcraft installed? Aweseome! If you could turn this into a system we could get the word out to hostbot services so that they can support this.
     
  15. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,793
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    I've decided to do something with this. I updated the first post with my current system code. I'm posting here until I finish everything.

    Here's a proper example (included in demo map) of how to do an HTTP request with at least only 1 player having SharpCraft.

    Code (vJASS):

    scope HttpExample initializer Init
       
         private function OnDownloadString takes nothing returns boolean
            local HttpRequest req = GetEventHttpRequest() // or HttpRequest.last()
         
            call BJDebugMsg(GetPlayerName(req.player) + " downloaded \"" + req.response + "\"")
         
            call req.destroy()
         
            return false
        endfunction
       
       
        private function OnMapStart takes nothing returns nothing
            local HttpRequest http = HttpRequest.create(FindPlayerWithSharpCraft(), "www.hiveworkshop.com/attachments/hello-txt.248392/")

            set http.callback = Filter(function OnDownloadString)
         
            call BJDebugMsg("Downloading " + http.url)
         
            call http.start()
        endfunction

        //===========================================================================
        private function Init takes nothing returns nothing
            call TimerStart(CreateTimer(), 1, false, function OnMapStart)
        endfunction

    endscope
     


    I'm not sure if hostbots can run SharpCraft. I think it actually needs to run the game client. I could be wrong though.
     
    Last edited: Sep 15, 2016
  16. Waffle

    Waffle

    Joined:
    Jul 30, 2013
    Messages:
    280
    Resources:
    0
    Resources:
    0
    if somebody could make a version of ghost++ that had an api to make map_callable plugins in a not-terrible way.. that would be awesome.
    and like http get / post would prolly be some of the most useful.. also a way to write/read server side data would be awfully nice for autoload-like systems if ppl change computers
     
  17. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,793
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    I'm already working on a autoload system where save codes are stored on my server. I will be releasing it soon.

    and yeah ghost support would be nice
     
  18. Waffle

    Waffle

    Joined:
    Jul 30, 2013
    Messages:
    280
    Resources:
    0
    Resources:
    0
    kind of sad if it only works with a custo m war3.exe tho.
    would it be possible to like abuse preload natives to do networking too? like with those windows nfs paths or sth?
     
  19. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,793
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    I think the simplest solution would be a .mix file in the war3 directory.

    But only 1 player needs to have it, so it's not too bad. It's also completely optional.
     
  20. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,793
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    Updated main post with current system code and a demo map.

    In the demo map your character's progress (items, stats, level) will automatically be saved online. Then when you re-play the game, your hero will be loaded. This works for all players who are in the map, as long as someone is running SharpCraft.

    [​IMG]