Created
March 8, 2023 20:32
-
-
Save Valkrysa/2d103cb910d2cac655e995013b6c969f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
https://www.hiveworkshop.com/pastebin/41be696537187bb3b209c20dafeb2a81.16058 | |
JASS: | |
library W3HMC /* | |
****************************************************************** | |
* | |
* v1.0.1 Beta, by TriggerHappy | |
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ | |
* W3HMC allows maps to communicate with hostbots. Generally this code | |
* should be used by libraries which provide API for specific functionality | |
* like WebRequest. | |
* _________________________________________________________________________ | |
* 1. Script Installation | |
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ | |
* Copy this script to your map and save it. | |
* _________________________________________________________________________ | |
* 2. Function API | |
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ | |
* function IsHMCEnabled takes nothing returns boolean | |
* function OnHMCInit takes boolexpr func returns nothing | |
* function GetTriggerHMCRequest takes nothing returns HMCRequest | |
* function TriggerRegisterHostbotEvent takes trigger whichTrigger, integer eventId returns nothing | |
* _________________________________________________________________________ | |
* 3. Struct API | |
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ | |
* struct HMCRequest | |
* | |
* static method create takes integer reqType returns thistype // reqType should be one of the request type constants defined in this library. For example, W3HMC_REQUEST_HTTP. | |
* | |
* method destroy takes nothing returns nothing | |
* method addArg takes string arg, string value returns nothing // Add arguments to the request | |
* method sendFrom takes player anyPlayer returns boolean // Send the request to the hostbot from a player. Using GetLocalPlayer will force the hostbot to use the player with the lowest latency. | |
* method getResponse takes nothing returns string // Get the response from the hostbot. | |
* | |
* boolexpr onComplete // The function to be executed when a response is received. | |
* | |
* readonly integer reqType | |
* readonly boolean gotResponse | |
* readonly player sendingPlayer | |
* _________________________________________________________________________ | |
* 4. Constants | |
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ | |
* constant integer EVENT_W3HMC_RESPONSE = 1 | |
* | |
* constant integer W3HMC_REQUEST_INIT = 1 | |
* constant integer W3HMC_REQUEST_HTTP = 2 | |
* constant integer W3HMC_REQUEST_PLAYERREALM = 3 | |
* | |
* constant integer W3HMC_ACTION_SET_ARGS = 1 | |
* constant integer W3HMC_ACTION_EXECUTE = 2 | |
* _________________________________________________________________________ | |
* 5. Example | |
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ | |
* function CheckHMC takes nothing returns nothing | |
* local HMCRequest req = GetTriggerHMCRequest() | |
* set HMCEnabled = (req.getResponse() == "Ok.") | |
* call req.destroy() | |
* endfunction | |
* | |
* // check if the hostbot supports HMC | |
* local HMCRequest req = HMCRequest.create(W3HMC_REQUEST_INIT) | |
* set req.onComplete = Filter(function CheckHMC) | |
* call req.sendFrom(GetLocalPlayer()) // using GetLocalPlayer allows the hostbot to use the fastest player | |
* _________________________________________________________________________ | |
* 6. How it works | |
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ | |
* 1. Hostbot generates a fake player. | |
* 2. Map communicates data to the hostbot through the gamecache sync natives. | |
* 3. Hostbot parses the gamecache action and checks for a valid HMC request. | |
* 4. Hostbot sends a response to the map through a chat message action from the fake player. | |
* | |
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ | |
*/ requires AAAAAW3HMCLib | |
endlibrary | |
// JassHelper orders libraries alphabetically. | |
// To ensure this libraries globals are registered first, we prefix the library name with A's. | |
// If you don't like this, you will have to edit your hostbot config to use the proper | |
// trigger and player id by setting bot_w3hmcdebug = 1, playing as W3HMC_PLAYER and typing a chat message | |
// to receive the values. | |
library AAAAAW3HMCLib | |
globals | |
// Config | |
constant string W3HMC_CACHE_FILENAME = "W" // shorter names are faster on the network | |
constant player W3HMC_PLAYER = Player(4) // fake player generated by the hostbot | |
private constant real INIT_TIMEOUT = 5.00 // how long to wait before the system decides it's disabled | |
// Constants | |
constant integer EVENT_W3HMC_RESPONSE = 1 | |
constant integer W3HMC_REQUEST_INIT = 1 | |
constant integer W3HMC_REQUEST_HTTP = 2 | |
constant integer W3HMC_REQUEST_PLAYERREALM = 3 | |
constant integer W3HMC_REQUEST_DATETIME = 4 | |
constant integer W3HMC_ACTION_SET_ARGS = 1 | |
constant integer W3HMC_ACTION_EXECUTE = 2 | |
// These need to be the first two handles created in your map | |
constant trigger W3HMC_TRIGGER = CreateTrigger() | |
constant event W3HMCEvent = TriggerRegisterPlayerChatEvent(W3HMC_TRIGGER, W3HMC_PLAYER, "", false) | |
// System vars | |
public boolean Initialized = false | |
private string array LastResponse | |
private real EventValue = -1 | |
private HMCRequest LastRequest = 0 | |
private trigger ExecTrig = CreateTrigger() | |
private HMCRequest InitRequest | |
private boolean Enabled = false | |
private trigger InitTrigger = CreateTrigger() | |
endglobals | |
private keyword ModuleInitializer | |
function IsHMCEnabled takes nothing returns boolean | |
return Enabled | |
endfunction | |
function OnHMCInit takes boolexpr func returns nothing | |
call TriggerAddCondition(InitTrigger, func) | |
endfunction | |
function GetTriggerHMCRequest takes nothing returns HMCRequest | |
return LastRequest | |
endfunction | |
function TriggerRegisterHostbotEvent takes trigger whichTrigger, integer eventId returns nothing | |
call TriggerRegisterVariableEvent(whichTrigger, SCOPE_PRIVATE + "EventValue", EQUAL, eventId) | |
endfunction | |
struct HMCRequest | |
readonly integer reqType | |
readonly boolean gotResponse | |
readonly player sendingPlayer | |
private string reqKey | |
private string args | |
private boolean running | |
boolexpr onComplete | |
readonly static gamecache Cache | |
static method create takes integer reqType returns thistype | |
local thistype this = thistype.allocate() | |
set this.reqType = reqType | |
set this.reqKey = I2S(reqType) | |
set this.gotResponse = false | |
set this.running = false | |
set this.args = "" | |
set this.onComplete = null | |
set LastResponse[this] = null | |
return this | |
endmethod | |
method destroy takes nothing returns nothing | |
set LastResponse[this] = null | |
set this.gotResponse = false | |
set this.running = false | |
set this.args = "" | |
call this.deallocate() | |
endmethod | |
method addArg takes string arg, string value returns nothing | |
set this.args = this.args + arg + " " + value + " " | |
endmethod | |
method sendFrom takes player anyPlayer returns boolean | |
local string gckey = I2S(this) | |
local string s = "" | |
if (LastResponse[this] != null or this.running) then | |
return false | |
endif | |
set this.running = true | |
set this.sendingPlayer = anyPlayer | |
if (this.args == null) then | |
set this.args = "" | |
elseif (StringLength(this.args) > 0) then | |
set this.args = SubString(this.args, 0, StringLength(this.args)-1) // trim last space | |
endif | |
if (StringLength(this.args) > 0) then | |
call StoreInteger(.Cache, gckey, this.args, W3HMC_ACTION_SET_ARGS) | |
endif | |
call StoreInteger(.Cache, gckey, this.reqKey, W3HMC_ACTION_EXECUTE) | |
if (GetLocalPlayer() == anyPlayer) then | |
if (StringLength(this.args) > 0) then | |
call SyncStoredInteger(.Cache, gckey, this.args) | |
endif | |
call SyncStoredInteger(.Cache, gckey, this.reqKey) | |
endif | |
return true | |
endmethod | |
method getResponse takes nothing returns string | |
return LastResponse[this] | |
endmethod | |
private static method OnChat takes nothing returns nothing | |
local string s = GetEventPlayerChatString() | |
local integer len = StringLength(s) | |
local integer i = 0 | |
local string char | |
local string value = "" | |
local integer reqId = 0 | |
local thistype req | |
// Parse the request ID from the response | |
loop | |
exitwhen i >= len | |
// Read the response one character at a time | |
set char = SubString(s, i, i + 1) | |
// We assume the request ID is stored before the first space | |
if (char == " ") then | |
if (reqId == 0) then | |
set reqId = S2I(value) | |
set value = "" | |
endif | |
else | |
set value = value + char | |
endif | |
set i = i + 1 | |
endloop | |
set req = thistype(reqId) | |
// Trim the request ID from the response | |
set len = StringLength(I2S(reqId)) | |
set LastResponse[reqId] = LastResponse[reqId] + SubString(s, len+1, StringLength(s)) | |
// Finalize | |
set req.gotResponse = true | |
// Execute events | |
set LastRequest = thistype(reqId) | |
set EventValue = EVENT_W3HMC_RESPONSE | |
set EventValue = -1 | |
if (req.onComplete != null) then | |
call TriggerClearConditions(ExecTrig) | |
call TriggerAddCondition(ExecTrig, req.onComplete) | |
call TriggerEvaluate(ExecTrig) | |
endif | |
endmethod | |
private static method checkTimeout takes nothing returns nothing | |
if (Initialized) then | |
return | |
endif | |
set Enabled = false | |
set Initialized = true | |
call DestroyTimer(GetExpiredTimer()) | |
call TriggerEvaluate(InitTrigger) | |
endmethod | |
private static method checkCallback takes nothing returns nothing | |
local HMCRequest req = GetTriggerHMCRequest() | |
set Enabled = (req.getResponse() == "Ok.") | |
call req.destroy() | |
set Initialized = true | |
call TriggerEvaluate(InitTrigger) | |
endmethod | |
private static method onMapStart takes nothing returns nothing | |
set InitRequest = HMCRequest.create(W3HMC_REQUEST_INIT) | |
set InitRequest.onComplete = Filter(function thistype.checkCallback) | |
call InitRequest.sendFrom(GetLocalPlayer()) // using GetLocalPlayer allows the hostbot to use the fastest player | |
call TimerStart(GetExpiredTimer(), INIT_TIMEOUT, false, function thistype.checkTimeout) | |
endmethod | |
implement ModuleInitializer | |
endstruct | |
private module ModuleInitializer | |
private static method onInit takes nothing returns nothing | |
call FlushGameCache(InitGameCache(W3HMC_CACHE_FILENAME)) | |
set .Cache = InitGameCache(W3HMC_CACHE_FILENAME) | |
call TriggerAddAction(W3HMC_TRIGGER, function HMCRequest.OnChat) | |
call TimerStart(CreateTimer(), 0.00, false, function thistype.onMapStart) | |
// prevent optimizers from removing or inlining the event | |
if (W3HMCEvent == null and W3HMCEvent == null) then | |
endif | |
endmethod | |
endmodule | |
endlibrary | |
Last edited: Jul 4, 2018 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment