diff --git a/README.md b/README.md index 813858e..fbbf89c 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ # Patchwire for GameMaker: Studio GameMaker client scripts for the Patchwire multiplayer server framework -Version 1.1.0 +Version 2.0.0 -Compatible with [Patchwire 0.2.*](https://github.com/twisterghost/patchwire). +Compatible with [Patchwire 0.5.*](https://github.com/twisterghost/patchwire). ## Installation -Download the latest .zip [release](https://github.com/twisterghost/patchwire/releases) of Patchwire's scripts and add them into your GameMaker project. +Download the latest .yymps [release](https://github.com/twisterghost/patchwire/releases) of Patchwire and import the local package. For detailed instructions, see [this guide](https://gmcore.io/installing.html) ## Usage @@ -24,15 +24,18 @@ This will initialize the client networking and connect to the given Patchwire se ```GML // obj_network_manager - Create -net_cmd_add_handler("connected", handle_connected); +net_cmd_add_handler("connected", function(data) { + status = "Connected"; + name = data[? "name"]; +}); ``` ```GML // obj_network_manager - Networking -net_cmd_resolve(); +net_resolve(); ``` -In the create event, we specify a handler script (`handle_connected`) for the `connected` command. In the networking event, we tell the Patchwire client to handle incoming commands. In this case, when the `connected` command is received, its contents will be sent to the `handle_connected` script, which can do whatever you please. `argument0` in the handler script will be the ID of a `ds_map` containing the data sent from the server. +In the create event, we specify a handler function for the `connected` command. In the networking event, we tell the Patchwire client to handle incoming commands with `net_resolve()`. In this case, when the `connected` command is received, its contents will be sent to the provided function, which can do whatever you please. In this case, we're just setting some variables. The first parameter of the handler function will be a ds_map of data from the server. This map will be automatically destroyed after all handlers run. #### Writing command handler scripts @@ -49,15 +52,13 @@ Lets use the example of writing a handler for a `chat` command from the server. Patchwire will route this command into the handler, providing the command JSON as a `ds_map`, so we can handle it like this: ```GML -// Script: handle_chat -var data = argument0; -var fromUser = data[? "user"]; -var message = data[? "message"]; +net_cmd_add_handler("chat", function(data) { + show_message(data[? "user"] + ': ' + data[? "message"]); +}); -show_message(fromUser + ': ' + message); ``` -You do not need to handle deleting the data map after your handler script runs. Patchwire cleans it up for you. +Again, you do not need to handle deleting the data map after your handler script runs. Patchwire cleans it up for you. #### Sending commands to the server @@ -74,8 +75,6 @@ command[? "someKey"] = "someValue"; net_cmd_send(command); ``` -That is all you need to do in order to send a command to the server. You can add as much or as little data to the command as you like. - > **NOTE:** If you don't provide the `command` value in `net_cmd_init`, Patchwire will just send whatever the most recently created command was. # Docs diff --git a/src/patchwire-gm/objects/obj_networking/Alarm_0.gml b/src/patchwire-gm/objects/obj_networking/Alarm_0.gml new file mode 100644 index 0000000..30991ef --- /dev/null +++ b/src/patchwire-gm/objects/obj_networking/Alarm_0.gml @@ -0,0 +1,6 @@ +/// @description attempt to connect + +status = "Connecting."; +connecting = true; +net_connect("127.0.0.1", 3001); +alarm[1] = 20; diff --git a/src/patchwire-gm/objects/obj_networking/Alarm_1.gml b/src/patchwire-gm/objects/obj_networking/Alarm_1.gml new file mode 100644 index 0000000..3e0315c --- /dev/null +++ b/src/patchwire-gm/objects/obj_networking/Alarm_1.gml @@ -0,0 +1,6 @@ +/// @description +if (connecting) { + status += "."; + alarm[1] = 20; +} + diff --git a/src/patchwire-gm/objects/obj_networking/Create_0.gml b/src/patchwire-gm/objects/obj_networking/Create_0.gml index 4faa767..2b5d691 100644 --- a/src/patchwire-gm/objects/obj_networking/Create_0.gml +++ b/src/patchwire-gm/objects/obj_networking/Create_0.gml @@ -1,19 +1,48 @@ -/// @description +/// @description set up net_init(); -playerId = "Unregistered"; +username = "Unnamed"; +registered = false; messageOfTheDay = "No MotD"; +status = "Idle"; +connecting = false; net_cmd_add_handler("register", function(data) { - playerId = data[? "playerId"]; + username = data[? "username"]; + registered = data[? "registered"]; + status = "Registered. Press to disconnect."; }); -net_cmd_add_handler("joined", function(data) { +net_cmd_add_handler("welcome", function(data) { messageOfTheDay = data[? "motd"]; + show_message("Message of the day:\n" + messageOfTheDay); + var username = get_string("Connected! Enter a username.", "Player 1"); + status = "Registering..."; + var cmd = net_cmd_init("register"); + cmd[? "username"] = username; net_cmd_send(cmd); }); +net_cmd_add_handler("disconnected", function() { + status = "Gracefully disconnected."; +}); + +net_cmd_add_handler("dropped", function() { + status = "Connection abruptly dropped."; +}); + +net_cmd_add_handler("connectFailed", function() { + status = "Failed to connect."; + connecting = false; +}); + +net_cmd_add_handler("connected", function() { + connecting = false; + status = "Connected."; +}); + + +alarm[0] = 10; -net_connect("127.0.0.1", 3001); \ No newline at end of file diff --git a/src/patchwire-gm/objects/obj_networking/Draw_0.gml b/src/patchwire-gm/objects/obj_networking/Draw_0.gml index 22bee67..3736140 100644 --- a/src/patchwire-gm/objects/obj_networking/Draw_0.gml +++ b/src/patchwire-gm/objects/obj_networking/Draw_0.gml @@ -1,3 +1,5 @@ -/// @description -draw_text(10, 10, playerId); -draw_text(10, 30, "Message of the day: " + messageOfTheDay); \ No newline at end of file +/// @description draw info +draw_text(10, 10, username); +draw_text(10, 30, "Registration status: " + string(registered)); +draw_text(10, 50, "Message of the day: " + messageOfTheDay); +draw_text(10, 70, status); \ No newline at end of file diff --git a/src/patchwire-gm/objects/obj_networking/KeyPress_32.gml b/src/patchwire-gm/objects/obj_networking/KeyPress_32.gml new file mode 100644 index 0000000..bf5b319 --- /dev/null +++ b/src/patchwire-gm/objects/obj_networking/KeyPress_32.gml @@ -0,0 +1,3 @@ +/// @description disconnect +status = "Disconnecting..."; +net_disconnect(); \ No newline at end of file diff --git a/src/patchwire-gm/objects/obj_networking/Other_68.gml b/src/patchwire-gm/objects/obj_networking/Other_68.gml index 77a3a01..2eb3af2 100644 --- a/src/patchwire-gm/objects/obj_networking/Other_68.gml +++ b/src/patchwire-gm/objects/obj_networking/Other_68.gml @@ -1,2 +1 @@ -/// @description -net_cmd_resolve(); \ No newline at end of file +net_resolve(); \ No newline at end of file diff --git a/src/patchwire-gm/objects/obj_networking/obj_networking.yy b/src/patchwire-gm/objects/obj_networking/obj_networking.yy index e6cd382..977985c 100644 --- a/src/patchwire-gm/objects/obj_networking/obj_networking.yy +++ b/src/patchwire-gm/objects/obj_networking/obj_networking.yy @@ -21,6 +21,9 @@ {"isDnD":false,"eventNum":0,"eventType":0,"collisionObjectId":null,"parent":{"name":"obj_networking","path":"objects/obj_networking/obj_networking.yy",},"resourceVersion":"1.0","name":"","tags":[],"resourceType":"GMEvent",}, {"isDnD":false,"eventNum":68,"eventType":7,"collisionObjectId":null,"parent":{"name":"obj_networking","path":"objects/obj_networking/obj_networking.yy",},"resourceVersion":"1.0","name":"","tags":[],"resourceType":"GMEvent",}, {"isDnD":false,"eventNum":0,"eventType":8,"collisionObjectId":null,"parent":{"name":"obj_networking","path":"objects/obj_networking/obj_networking.yy",},"resourceVersion":"1.0","name":"","tags":[],"resourceType":"GMEvent",}, + {"isDnD":false,"eventNum":32,"eventType":9,"collisionObjectId":null,"parent":{"name":"obj_networking","path":"objects/obj_networking/obj_networking.yy",},"resourceVersion":"1.0","name":"","tags":[],"resourceType":"GMEvent",}, + {"isDnD":false,"eventNum":0,"eventType":2,"collisionObjectId":null,"parent":{"name":"obj_networking","path":"objects/obj_networking/obj_networking.yy",},"resourceVersion":"1.0","name":"","tags":[],"resourceType":"GMEvent",}, + {"isDnD":false,"eventNum":1,"eventType":2,"collisionObjectId":null,"parent":{"name":"obj_networking","path":"objects/obj_networking/obj_networking.yy",},"resourceVersion":"1.0","name":"","tags":[],"resourceType":"GMEvent",}, ], "properties": [], "overriddenProperties": [], diff --git a/src/patchwire-gm/patchwire-gm.yyp b/src/patchwire-gm/patchwire-gm.yyp index 120bca1..5b368a0 100644 --- a/src/patchwire-gm/patchwire-gm.yyp +++ b/src/patchwire-gm/patchwire-gm.yyp @@ -1,14 +1,10 @@ { "resources": [ - {"id":{"name":"net_cmd_resolve","path":"scripts/net_cmd_resolve/net_cmd_resolve.yy",},"order":0,}, - {"id":{"name":"net_cmd_send","path":"scripts/net_cmd_send/net_cmd_send.yy",},"order":1,}, - {"id":{"name":"net_connect","path":"scripts/net_connect/net_connect.yy",},"order":2,}, {"id":{"name":"obj_networking","path":"objects/obj_networking/obj_networking.yy",},"order":0,}, - {"id":{"name":"net_handle_command","path":"scripts/net_handle_command/net_handle_command.yy",},"order":3,}, - {"id":{"name":"net_init","path":"scripts/net_init/net_init.yy",},"order":4,}, - {"id":{"name":"net_cmd_add_handler","path":"scripts/net_cmd_add_handler/net_cmd_add_handler.yy",},"order":5,}, - {"id":{"name":"net_cmd_init","path":"scripts/net_cmd_init/net_cmd_init.yy",},"order":6,}, - {"id":{"name":"net_cmd_parse","path":"scripts/net_cmd_parse/net_cmd_parse.yy",},"order":7,}, + {"id":{"name":"net_utils","path":"scripts/net_utils/net_utils.yy",},"order":8,}, + {"id":{"name":"net_connection","path":"scripts/net_connection/net_connection.yy",},"order":4,}, + {"id":{"name":"net_commands","path":"scripts/net_commands/net_commands.yy",},"order":6,}, + {"id":{"name":"net_event_management","path":"scripts/net_event_management/net_event_management.yy",},"order":7,}, {"id":{"name":"room0","path":"rooms/room0/room0.yy",},"order":0,}, ], "Options": [ diff --git a/src/patchwire-gm/scripts/net_cmd_add_handler/net_cmd_add_handler.gml b/src/patchwire-gm/scripts/net_cmd_add_handler/net_cmd_add_handler.gml deleted file mode 100644 index 1a04308..0000000 --- a/src/patchwire-gm/scripts/net_cmd_add_handler/net_cmd_add_handler.gml +++ /dev/null @@ -1,18 +0,0 @@ -/// @param Add a method to run when a given command type is received -/// @param {String} CommandType -/// @param {Method} HandlerFunction -/// Note: You can add multiple handlers for the given command type with multiple calls to this script -function net_cmd_add_handler(commandType, handlerFunction) { - - // If we have a handler array already, append this handler. - // Otherwise, add an entry to the map. - if (ds_map_exists(global.patchwire_netHandlerMap, commandType)) { - var handlerList = ds_map_find_value(global.netHandlerMap, commandType); - handlerList[array_length(handlerList)] = handlerFunction; - ds_map_replace(global.patchwire_netHandlerMap, commandType, handlerList); - } else { - var handlerList; - handlerList[0] = handlerFunction; - ds_map_add(global.patchwire_netHandlerMap, commandType, handlerList); - } -} diff --git a/src/patchwire-gm/scripts/net_cmd_init/net_cmd_init.gml b/src/patchwire-gm/scripts/net_cmd_init/net_cmd_init.gml deleted file mode 100644 index cc722ab..0000000 --- a/src/patchwire-gm/scripts/net_cmd_init/net_cmd_init.gml +++ /dev/null @@ -1,12 +0,0 @@ -/// @desc Initializes a command to send to the server. -/// @param {String} CommandType -/// @returns ds_map to add data to before sending the command -function net_cmd_init(argument0) { - global.patchwire_netCurrentData = ds_map_create(); - ds_map_add(global.patchwire_netCurrentData, "command", argument0); - return global.patchwire_netCurrentData; - - - - -} diff --git a/src/patchwire-gm/scripts/net_cmd_init/net_cmd_init.yy b/src/patchwire-gm/scripts/net_cmd_init/net_cmd_init.yy deleted file mode 100644 index 4004d0e..0000000 --- a/src/patchwire-gm/scripts/net_cmd_init/net_cmd_init.yy +++ /dev/null @@ -1,12 +0,0 @@ -{ - "isDnD": false, - "isCompatibility": false, - "parent": { - "name": "patchwire", - "path": "folders/Scripts/patchwire.yy", - }, - "resourceVersion": "1.0", - "name": "net_cmd_init", - "tags": [], - "resourceType": "GMScript", -} \ No newline at end of file diff --git a/src/patchwire-gm/scripts/net_cmd_parse/net_cmd_parse.gml b/src/patchwire-gm/scripts/net_cmd_parse/net_cmd_parse.gml deleted file mode 100644 index 3f1124d..0000000 --- a/src/patchwire-gm/scripts/net_cmd_parse/net_cmd_parse.gml +++ /dev/null @@ -1,57 +0,0 @@ -function _net_util_split(inputString, splitter) { - var splitterLength = string_length(splitter); - var result; - var splitterLocation; - var part; - var count = 0; - - while (string_pos(splitter, inputString) > 0) { - splitterLocation = string_pos(splitter, inputString); - part = string_copy(inputString, 1, splitterLocation - 1); - result[count] = part; - count++; - inputString = string_delete(inputString, 1, splitterLocation + splitterLength - 1); - } - - result[count] = inputString; - return result; -} - -/// @desc Reads data from async_load and returns a map of the data sent from the server. -/// Note: This is for internal use in Patchwire -function net_cmd_parse() { - var netResponseType = ds_map_find_value(async_load, "type"); - - switch (netResponseType) { - case network_type_data: - var netResponseBuffer = ds_map_find_value(async_load, "buffer"); - var netResponseData = buffer_read(netResponseBuffer, buffer_string); - buffer_seek(netResponseBuffer, buffer_seek_start, 0); - - // Check if this buffer has multiple commands - var commandChunks = _net_util_split(netResponseData, "\n\t\n"); - var chunkCount = array_length(commandChunks); - var netResponseMap = ds_map_create(); - netResponseMap[? "batch"] = true; - netResponseMap[? "commands"] = ds_list_create(); - for (var i = 0; i < chunkCount; i++) { - ds_list_add(netResponseMap[? "commands"], json_decode(commandChunks[i])); - } - - buffer_delete(netResponseBuffer); - return netResponseMap; - case network_type_connect: - case network_type_non_blocking_connect: - global.patchwire_connectedStatus = async_load[? "succeeded"]; - - if (async_load[? "succeeded"]) { - return NetEvent.Connect; - } - return NetEvent.ConnectFail; - case network_type_disconnect: - global.patchwire_connectedStatus = false; - return NetEvent.Disconnect; - default: - return NetEvent.Unknown; - } -} diff --git a/src/patchwire-gm/scripts/net_cmd_resolve/net_cmd_resolve.gml b/src/patchwire-gm/scripts/net_cmd_resolve/net_cmd_resolve.gml deleted file mode 100644 index 661a6fe..0000000 --- a/src/patchwire-gm/scripts/net_cmd_resolve/net_cmd_resolve.gml +++ /dev/null @@ -1,38 +0,0 @@ -/// @desc Reads through registered handlers and runs them if it finds a command match. -/// Note: Call this on a network management object in it's Networking event. -function net_cmd_resolve() { - - var netResponse = net_cmd_parse(); - - - if (netResponse >= 0) { - if (ds_map_exists(netResponse, "batch")) { - var commandList = ds_map_find_value(netResponse, "commands"); - var commandCount = ds_list_size(commandList); - var thisCommand, thisCommandMap; - - for (var i = 0; i < commandCount; i++) { - thisCommandMap = ds_list_find_value(commandList, i); - thisCommand = ds_map_find_value(thisCommandMap, "command"); - net_handle_command(thisCommand, thisCommandMap); - ds_map_destroy(thisCommandMap); - } - - ds_list_destroy(commandList); - } else { - var command = ds_map_find_value(netResponse, "command"); - net_handle_command(command, netResponse); - } - - if (ds_exists(netResponse, ds_type_map)) { - ds_map_destroy(netResponse); - } - - } else { - if (netResponse == NetEvent.ConnectFail) { - net_handle_command("connectFailed", ""); - } else if (netResponse == NetEvent.Disconnect) { - net_handle_command("disconneded", ""); - } - } -} diff --git a/src/patchwire-gm/scripts/net_cmd_resolve/net_cmd_resolve.yy b/src/patchwire-gm/scripts/net_cmd_resolve/net_cmd_resolve.yy deleted file mode 100644 index 0a79dba..0000000 --- a/src/patchwire-gm/scripts/net_cmd_resolve/net_cmd_resolve.yy +++ /dev/null @@ -1,12 +0,0 @@ -{ - "isDnD": false, - "isCompatibility": false, - "parent": { - "name": "patchwire", - "path": "folders/Scripts/patchwire.yy", - }, - "resourceVersion": "1.0", - "name": "net_cmd_resolve", - "tags": [], - "resourceType": "GMScript", -} \ No newline at end of file diff --git a/src/patchwire-gm/scripts/net_cmd_send/net_cmd_send.gml b/src/patchwire-gm/scripts/net_cmd_send/net_cmd_send.gml deleted file mode 100644 index 8770efa..0000000 --- a/src/patchwire-gm/scripts/net_cmd_send/net_cmd_send.gml +++ /dev/null @@ -1,31 +0,0 @@ -/// @desc Sends the command to the server as JSON -/// @param {ds_map} OptionalCommandID -/// @param {Boolean} OptionalPreventDestroy -/// Note: If no argument is passed in, uses the most recently created net data -/// Note: Destroys the map passed in unless "OptionalPreventDestroy" is set to true -function net_cmd_send() { - - var dataMap = global.patchwire_netCurrentData; - - if (argument_count > 0) { - dataMap = argument[0]; - } - - // Encode the content to JSON - var contentToSend = json_encode(dataMap); - - // Create the content buffer - var buffer = buffer_create(1, buffer_grow, 1); - buffer_seek(buffer, buffer_seek_start, 0); - buffer_write(buffer, buffer_string, contentToSend); - - // Send the content - network_send_raw(global.patchwire_netSock, buffer, buffer_get_size(buffer)); - - // Clean up - buffer_delete(buffer); - - if (argument_count <= 1 || !argument[1]) { - ds_map_destroy(dataMap); - } -} diff --git a/src/patchwire-gm/scripts/net_commands/net_commands.gml b/src/patchwire-gm/scripts/net_commands/net_commands.gml new file mode 100644 index 0000000..4231c25 --- /dev/null +++ b/src/patchwire-gm/scripts/net_commands/net_commands.gml @@ -0,0 +1,90 @@ +/// @desc Creates a command to send to the server +/// @param {String} CommandType The command type for the command +/// @param {ds_map} [CommandData] An optional map of data to convert into the command +/// @returns ds_map to add data to before sending the command +function net_cmd_init() { + if (argument_count == 2) { + global.patchwire_netCurrentData = argument[1]; + } else { + global.patchwire_netCurrentData = ds_map_create(); + } + + ds_map_add(global.patchwire_netCurrentData, "command", argument[0]); + return global.patchwire_netCurrentData; +} + +/// @desc Sends the command to the server as JSON +/// @param {ds_map} OptionalCommandID +/// @param {Boolean} OptionalPreventDestroy +/// Note: If no argument is passed in, uses the most recently created net data +/// Note: Destroys the map passed in unless "OptionalPreventDestroy" is set to true +function net_cmd_send() { + + var dataMap = global.patchwire_netCurrentData; + + if (argument_count > 0) { + dataMap = argument[0]; + } + + // Encode the content to JSON + var contentToSend = json_encode(dataMap); + + // Create the content buffer + var buffer = buffer_create(1, buffer_grow, 1); + buffer_seek(buffer, buffer_seek_start, 0); + buffer_write(buffer, buffer_string, contentToSend); + + // Send the content + network_send_raw(global.patchwire_netSock, buffer, buffer_get_size(buffer)); + + // Clean up + buffer_delete(buffer); + + if (argument_count <= 1 || !argument[1]) { + ds_map_destroy(dataMap); + } +} + + +/// @param Add a method to run when a given command type is received +/// @param {String} CommandType +/// @param {Method} HandlerFunction +/// Note: You can add multiple handlers for the given command type with multiple calls to this script +function net_cmd_add_handler(commandType, handlerFunction) { + + // If we have a handler array already, append this handler. + // Otherwise, add an entry to the map. + if (ds_map_exists(global.patchwire_netHandlerMap, commandType)) { + var handlerList = ds_map_find_value(global.patchwire_netHandlerMap, commandType); + handlerList[array_length(handlerList)] = handlerFunction; + ds_map_replace(global.patchwire_netHandlerMap, commandType, handlerList); + } else { + var handlerList; + handlerList[0] = handlerFunction; + ds_map_add(global.patchwire_netHandlerMap, commandType, handlerList); + } +} + +/// @desc Applies the handler function to the given command data as received from the server. +/// @param {String} CommandType +/// @param {ds_map} CommandData +/// Note: This is for internal use in Patchwire +function net_handle_command(commandType, commandData) { + if (commandType == "__net__handshake") { + var cmd = net_cmd_init("__net__handshake"); + net_cmd_send(cmd); + } else if (commandType == "__net__fin__ack") { + global.patchwire_readyToDisconnect = true; + net_destroy(); + net_handle_command("disconnected", ""); + } else if (ds_map_exists(global.patchwire_netHandlerMap, commandType)) { + var handlerList = ds_map_find_value(global.patchwire_netHandlerMap, commandType); + var handlerListLength = array_length(handlerList); + + for (var i = 0; i < handlerListLength; i++) { + var handler = handlerList[i] + handler(commandData); + } + } +} + diff --git a/src/patchwire-gm/scripts/net_cmd_send/net_cmd_send.yy b/src/patchwire-gm/scripts/net_commands/net_commands.yy similarity index 88% rename from src/patchwire-gm/scripts/net_cmd_send/net_cmd_send.yy rename to src/patchwire-gm/scripts/net_commands/net_commands.yy index 39cf5ae..1766e25 100644 --- a/src/patchwire-gm/scripts/net_cmd_send/net_cmd_send.yy +++ b/src/patchwire-gm/scripts/net_commands/net_commands.yy @@ -6,7 +6,7 @@ "path": "folders/Scripts/patchwire.yy", }, "resourceVersion": "1.0", - "name": "net_cmd_send", + "name": "net_commands", "tags": [], "resourceType": "GMScript", } \ No newline at end of file diff --git a/src/patchwire-gm/scripts/net_connect/net_connect.gml b/src/patchwire-gm/scripts/net_connect/net_connect.gml deleted file mode 100644 index d47cab3..0000000 --- a/src/patchwire-gm/scripts/net_connect/net_connect.gml +++ /dev/null @@ -1,11 +0,0 @@ -/// @desc Connects to a server via a socket. -/// @param {String} ServerIP An IP to connect to as a string. e.g. "127.0.0.1" -/// @param {Real} ServerPort A port as a number to connect on. e.g. 3000 -function net_connect(serverIP, serverPort) { - - var res = network_connect_raw(global.patchwire_netSock, serverIP, serverPort); - - if (res < 0) { - net_handle_command("connectFailed", ""); - } -} diff --git a/src/patchwire-gm/scripts/net_connect/net_connect.yy b/src/patchwire-gm/scripts/net_connect/net_connect.yy deleted file mode 100644 index c1e5b57..0000000 --- a/src/patchwire-gm/scripts/net_connect/net_connect.yy +++ /dev/null @@ -1,12 +0,0 @@ -{ - "isDnD": false, - "isCompatibility": false, - "parent": { - "name": "patchwire", - "path": "folders/Scripts/patchwire.yy", - }, - "resourceVersion": "1.0", - "name": "net_connect", - "tags": [], - "resourceType": "GMScript", -} \ No newline at end of file diff --git a/src/patchwire-gm/scripts/net_connection/net_connection.gml b/src/patchwire-gm/scripts/net_connection/net_connection.gml new file mode 100644 index 0000000..c00dac1 --- /dev/null +++ b/src/patchwire-gm/scripts/net_connection/net_connection.gml @@ -0,0 +1,60 @@ +enum NetEvent { + Command = 0, + Connect = -1, + Disconnect = -2, + ConnectFail = -3, + Unknown = -4 +} + +enum NetBlocking { + Block = 0, + Nonblocking = 1, +} + +/// @desc Initializes the net system. +/// @param {Real} optionalTimeout a connection timeout in milliseconds. Default: 4000 +/// @param {Boolean} optionalBlocking `true` to pause while connecting, false otherwise. Default: false +function net_init() { + var timeout = 4000; + var blocking = NetBlocking.Nonblocking; + + if (argument_count > 0) { + timeout = argument[0]; + } + + if (argument_count > 1) { + if (argument[1]) { + blocking = NetBlocking.Block; + } else { + blocking = NetBlocking.Nonblocking; + } + } + + network_set_config(network_config_connect_timeout, timeout); + network_set_config(network_config_use_non_blocking_socket, blocking); + global.patchwire_netSock = network_create_socket(network_socket_tcp); + global.patchwire_netHandlerMap = ds_map_create(); + global.patchwire_connectedStatus = false; + global.patchwire_readyToDisconnect = false; +} + +/// @desc Connects to a server via a socket. +/// @param {String} ServerIP An IP to connect to as a string. e.g. "127.0.0.1" +/// @param {Real} ServerPort A port as a number to connect on. e.g. 3000 +function net_connect(serverIP, serverPort) { + + var res = network_connect_raw(global.patchwire_netSock, serverIP, serverPort); + + if (res < 0) { + net_handle_command("connectFailed", ""); + } +} + +function net_disconnect() { + var finCmd = net_cmd_init("__net__fin"); + net_cmd_send(finCmd); +} + +function net_destroy() { + network_destroy(global.patchwire_netSock); +} \ No newline at end of file diff --git a/src/patchwire-gm/scripts/net_cmd_parse/net_cmd_parse.yy b/src/patchwire-gm/scripts/net_connection/net_connection.yy similarity index 88% rename from src/patchwire-gm/scripts/net_cmd_parse/net_cmd_parse.yy rename to src/patchwire-gm/scripts/net_connection/net_connection.yy index fa8ad2c..6795e54 100644 --- a/src/patchwire-gm/scripts/net_cmd_parse/net_cmd_parse.yy +++ b/src/patchwire-gm/scripts/net_connection/net_connection.yy @@ -6,7 +6,7 @@ "path": "folders/Scripts/patchwire.yy", }, "resourceVersion": "1.0", - "name": "net_cmd_parse", + "name": "net_connection", "tags": [], "resourceType": "GMScript", } \ No newline at end of file diff --git a/src/patchwire-gm/scripts/net_event_management/net_event_management.gml b/src/patchwire-gm/scripts/net_event_management/net_event_management.gml new file mode 100644 index 0000000..47baed0 --- /dev/null +++ b/src/patchwire-gm/scripts/net_event_management/net_event_management.gml @@ -0,0 +1,95 @@ +function _net_read_buffer(load_map) { + var netResponseBuffer = ds_map_find_value(load_map, "buffer"); + var netResponseData = buffer_read(netResponseBuffer, buffer_string); + buffer_seek(netResponseBuffer, buffer_seek_start, 0); + buffer_delete(netResponseBuffer); + return netResponseData; +} + +function _net_create_command_batch_from_raw(rawData) { + var commandChunks = _net_util_split(rawData, "\n\t\n"); + var chunkCount = array_length(commandChunks); + var netResponseMap = ds_map_create(); + netResponseMap[? "batch"] = true; + netResponseMap[? "commands"] = ds_list_create(); + for (var i = 0; i < chunkCount; i++) { + var decodedChunk = json_decode(commandChunks[i]); + + if (decodedChunk == -1) { + throw "NETWORKING ERROR: Failed to decode command"; + } + + ds_list_add(netResponseMap[? "commands"], decodedChunk); + } + + return netResponseMap; +} + +/// @desc Reads data from async_load and returns a map of the data sent from the server. +/// Note: This is for internal use in Patchwire +function net_cmd_parse() { + var netResponseType = ds_map_find_value(async_load, "type"); + + switch (netResponseType) { + case network_type_data: + var netResponseData = _net_read_buffer(async_load); + return _net_create_command_batch_from_raw(netResponseData); + case network_type_connect: + case network_type_non_blocking_connect: + global.patchwire_connectedStatus = async_load[? "succeeded"]; + + if (async_load[? "succeeded"]) { + return NetEvent.Connect; + } + return NetEvent.ConnectFail; + case network_type_disconnect: + global.patchwire_connectedStatus = false; + return NetEvent.Disconnect; + default: + return NetEvent.Unknown; + } +} + + +/// @desc Reads through registered handlers and runs them if it finds a command match. +/// Note: Call this on a network management object in it's Networking event. +function net_resolve() { + + var netResponse = net_cmd_parse(); + + if (netResponse >= 0) { + if (ds_map_exists(netResponse, "batch")) { + var commandList = ds_map_find_value(netResponse, "commands"); + var commandCount = ds_list_size(commandList); + var thisCommand, thisCommandMap; + + for (var i = 0; i < commandCount; i++) { + thisCommandMap = ds_list_find_value(commandList, i); + thisCommand = ds_map_find_value(thisCommandMap, "command"); + net_handle_command(thisCommand, thisCommandMap); + ds_map_destroy(thisCommandMap); + } + + ds_list_destroy(commandList); + } else { + var command = ds_map_find_value(netResponse, "command"); + net_handle_command(command, netResponse); + } + + if (ds_exists(netResponse, ds_type_map)) { + ds_map_destroy(netResponse); + } + + } else { + if (netResponse == NetEvent.ConnectFail) { + net_handle_command("connectFailed", ""); + } else if (netResponse == NetEvent.Disconnect) { + if (global.patchwire_readyToDisconnect) { + net_handle_command("disconnected", ""); + } else { + net_handle_command("dropped", ""); + } + } + } +} + diff --git a/src/patchwire-gm/scripts/net_cmd_add_handler/net_cmd_add_handler.yy b/src/patchwire-gm/scripts/net_event_management/net_event_management.yy similarity index 86% rename from src/patchwire-gm/scripts/net_cmd_add_handler/net_cmd_add_handler.yy rename to src/patchwire-gm/scripts/net_event_management/net_event_management.yy index 22414e5..ae074d4 100644 --- a/src/patchwire-gm/scripts/net_cmd_add_handler/net_cmd_add_handler.yy +++ b/src/patchwire-gm/scripts/net_event_management/net_event_management.yy @@ -6,7 +6,7 @@ "path": "folders/Scripts/patchwire.yy", }, "resourceVersion": "1.0", - "name": "net_cmd_add_handler", + "name": "net_event_management", "tags": [], "resourceType": "GMScript", } \ No newline at end of file diff --git a/src/patchwire-gm/scripts/net_handle_command/net_handle_command.gml b/src/patchwire-gm/scripts/net_handle_command/net_handle_command.gml deleted file mode 100644 index 9168a45..0000000 --- a/src/patchwire-gm/scripts/net_handle_command/net_handle_command.gml +++ /dev/null @@ -1,18 +0,0 @@ -/// @desc Applies the handler function to the given command data as received from the server. -/// @param {String} CommandType -/// @param {ds_map} CommandData -/// Note: This is for internal use in Patchwire -function net_handle_command(commandType, commandData) { - if (commandType == "__net__handshake") { - var cmd = net_cmd_init("__net__handshake"); - net_cmd_send(cmd); - } else if (ds_map_exists(global.patchwire_netHandlerMap, commandType)) { - var handlerList = ds_map_find_value(global.patchwire_netHandlerMap, commandType); - var handlerListLength = array_length(handlerList); - - for (var i = 0; i < handlerListLength; i++) { - var handler = handlerList[i] - handler(commandData); - } - } -} diff --git a/src/patchwire-gm/scripts/net_handle_command/net_handle_command.yy b/src/patchwire-gm/scripts/net_handle_command/net_handle_command.yy deleted file mode 100644 index d419e35..0000000 --- a/src/patchwire-gm/scripts/net_handle_command/net_handle_command.yy +++ /dev/null @@ -1,12 +0,0 @@ -{ - "isDnD": false, - "isCompatibility": false, - "parent": { - "name": "patchwire", - "path": "folders/Scripts/patchwire.yy", - }, - "resourceVersion": "1.0", - "name": "net_handle_command", - "tags": [], - "resourceType": "GMScript", -} \ No newline at end of file diff --git a/src/patchwire-gm/scripts/net_init/net_init.gml b/src/patchwire-gm/scripts/net_init/net_init.gml deleted file mode 100644 index 1de7f10..0000000 --- a/src/patchwire-gm/scripts/net_init/net_init.gml +++ /dev/null @@ -1,33 +0,0 @@ -/// @desc Initializes the net system. -/// @param {Real} optionalTimeout a connection timeout in milliseconds. Default: 4000 -/// @param {Boolean} optionalBlocking `true` to pause while connecting, false otherwise. Default: true -function net_init() { - var timeout = 4000; - var blocking = 0; - - if (argument_count > 0) { - timeout = argument[0]; - } - - if (argument_count > 1) { - if (argument[1]) { - blocking = 0; - } else { - blocking = 1; - } - } - - network_set_config(network_config_connect_timeout, timeout); - network_set_config(network_config_use_non_blocking_socket, blocking); - global.patchwire_netSock = network_create_socket(network_socket_tcp); - global.patchwire_netHandlerMap = ds_map_create(); - global.patchwire_connectedStatus = false; - - enum NetEvent { - Command = 0, - Connect = -1, - Disconnect = -2, - ConnectFail = -3, - Unknown = -4 - } -} diff --git a/src/patchwire-gm/scripts/net_utils/net_utils.gml b/src/patchwire-gm/scripts/net_utils/net_utils.gml new file mode 100644 index 0000000..f284a31 --- /dev/null +++ b/src/patchwire-gm/scripts/net_utils/net_utils.gml @@ -0,0 +1,19 @@ +// For internal use only +function _net_util_split(inputString, splitter) { + var splitterLength = string_length(splitter); + var result; + var splitterLocation; + var part; + var count = 0; + + while (string_pos(splitter, inputString) > 0) { + splitterLocation = string_pos(splitter, inputString); + part = string_copy(inputString, 1, splitterLocation - 1); + result[count] = part; + count++; + inputString = string_delete(inputString, 1, splitterLocation + splitterLength - 1); + } + + result[count] = inputString; + return result; +} diff --git a/src/patchwire-gm/scripts/net_init/net_init.yy b/src/patchwire-gm/scripts/net_utils/net_utils.yy similarity index 90% rename from src/patchwire-gm/scripts/net_init/net_init.yy rename to src/patchwire-gm/scripts/net_utils/net_utils.yy index 18835e2..0a7c401 100644 --- a/src/patchwire-gm/scripts/net_init/net_init.yy +++ b/src/patchwire-gm/scripts/net_utils/net_utils.yy @@ -6,7 +6,7 @@ "path": "folders/Scripts/patchwire.yy", }, "resourceVersion": "1.0", - "name": "net_init", + "name": "net_utils", "tags": [], "resourceType": "GMScript", } \ No newline at end of file