Your Solution to Multiplayer in ChoiceScript Games

From the charming genius who brought you twiger_functions, it’s time to meet dragonfly_functions. (link to the code)

Dragonfly allows you to much more easily implement multiplayer in your Choicescript Games.

Unlike twiger_functions, dragonfly_functions actually uses the *script command, making it virtually unpublishable. dragonfly_functions kinda relies on twiger_functions if you want to read the list of players or share entire arrays of data.


What it can be used for
  1. Simpler games like chess or battleship in ChoiceScript.
  2. Complex story-based/open world games where players can interact or simply have an effect on each other.
  3. in-game chat
First thing you have to do

Reply below and have some form of experience with ChoiceScript (I’ll check your profile for how much you’ve been on the forums, if you have any published stories/WIP). Then I’ll message you a dev key which is not to be shared with others.

Documentation
  1. Copy and paste or import dragonfly_functions.txt into your CS game as a scene file
  2. Add these variables to startup
*commentinput
*create dragonfly_dev "YourDevKey"
*create dragonfly_gameinstance ""
*create dragonfly_player ""
*create dragonfly_outgoingdata ""
*comment output
*create dragonfly_playercount 0
*create dragonfly_playerlist ""

Now all you have to do is use the following *gosubs:

*gosub_scene dragonfly_functions GetData_PageBreak
*gosub_scene dragonfly_functions SendAndGetData_PageBreak "data to send"
*gosub_scene dragonfly_functions GetData
*gosub_scene dragonfly_functions SendAndGetData "data to send"

Every time you use one of those four, the data in playercount, playerlist, and runtime variables that are name like thus: dragonfly_data_playername, are all set with data from the server.
If you’re sending data, then the string on the end will be sent to the server. Data on the server will almost always be deleted after 1 hour, but don’t think it’s impossible for older data to exist.

When you send data, you send

  1. Your dev ID This is specific to the author, so other authors’ data doesn’t mess with yours
  2. A gameinstance ID (like a game lobby) Players playing together share the same gameinstance ID. You can make this whatever you want but it must be <11 characters and it must be URL-safe
  3. The player’s ID This can be the player’s name, or just something like “player1”. It must be unique for each player in the gameinstance. The player ID must be URL-safe, variable-name safe, and <16 characters.
  4. The player’s data This is a string. It must be URL-safe and no more than 1000 characters long. If you put spaces in it, those spaces will temporarily be replaced with “~” symbols to make it URL-safe, thus any ~ you put in will be returned as spaces. I suggest you use the Twiger_Functions’ arrays to store tons of variables in this one string, but it’s still up to you to to keep it <1000 characters… there are workarounds I wont tell you about to make it longer per player because I’m using the free version of Wix to host all this.

When you receive data, you only get data for the matching dev ID and gameinstance ID (and most data gets deleted after it’s 1 hour old). The data you get:
dragonfly_playercount This is the number of players with data on the server. These players may have stopped playing 20 minutes ago, but who knows?
dragonfly_playerlist a carat (^ symbol) separated list of player names (try using Twiger_Functions). This is good for accessing the player’s data (or would be if CS would stop claiming variables don’t exist when they do) such as by doing: dragonfly_data[varaible_holding_player1’s_name]
dragonfly_data_??? A bunch of global variables where ??? is actually the player’s name. It hold’s that players data.

Note: when a player’s data is put on the server, it erases their old data.

Hopefully this is good enough documentation? idk how to tell people how it works.

When to use each function

GetData to only get data, SendAndGet to also tell the server what your player has been up to.
_pagebreak to automatically make the game wait until the server gives you the data before writing the page. As far as I know, you would always want to put SendAndGetData_PageBreak after a page break, choice, chapter end, etc and never use it without _PageBreak. Again I’m too tired to explain this so if you don’t know what you’re doing, here’s an example:

you will submit data now
*set dragonfly_gameinstance "6"
*set dragonfly_player "taco"

*page_break
*gosub_scene dragonfly_functions SendAndGetData_PageBreak "pizza"
There are a total of [b]${dragonfly_playercount}[/b] players.

player taco: ${dragonfly_data["taco"]}

player tusss: ${dragonfly_data["tusss"]}

playerlist: ${dragonfly_playerlist}

*set lists_listname "dragonfly_playerlist"
*set lists_index 2
*gosub_scene twiger_functions find
item 2 of that: ${lists_output}
The problem

CS doesn’t care if variables exist, it’ll still throw errors on your game if you don’t *create variables in the startup file (please lmk if you know a way around this with *script) with the player “names”, so use player names like player1 player2 etc I guess and declare them like *create dragonfly_data_player5 “”

I’m bad at writing documentation, sorry.

10 Likes

That is awesome. Most of it wen’t right over my head, but the actual idea? Super cool. I’m in awe you made choicescript games multiplayer haha

1 Like

Wow! I did something similar in my 3D maze (blocking on page_break etc.), but this is a very general solution, with a very nice interface. Amazing. Thanks for sharing, I am curious to see where people will take it.

BTW, I didn’t know you could run your own functions from wixsite.com. I was using kvdb.io

Link if you are curious: Two-player 3D game demo: The Dungeon of Lamurloq (first-person POV dungeon crawler)

1 Like

Your maze is what inspired me. I was going to ask you how you got around the need for a pagebreak before I figured out my workaround.
Wix lets you run backend functions and have a database so it’s a nice bundle for all my various needs.

1 Like

Oh, nice to hear, I’m really glad. By the way, nice workaround!

document.getElementsByClassName(“next”)[0].click();

About this issue:

CS doesn’t care if variables exist, it’ll still throw errors on your game if you don’t *create variables in the startup file

This seems to work for me. After you set a var with *script, you can use it regularly. But it looks like your code already does that - what was the exact problem?

*script this.stats.player=“Mr. X”;
Welcome, {player}. *set player "Mr. Y" Welcome, {player}.

huh… maybe it is because I’ve been referencing the variables with the array thingy ${dragonfly_data[“tusss”]} or something. idk. if other people don’t get the problem, good for them :stuck_out_tongue: .

1 Like

Oh, yes, arrays in Choicescript are just emulated. Try this:

*script this.stats.dragonfly_data_1="Tusss"; 
Welcome, ${dragonfly_data[1]}.
*temp idx 1
Welcome, ${dragonfly_data[idx]}.
*script this.stats.dragonfly_data_tusss="Tusss";
Welcome, ${dragonfly_data["tusss"]}.
*temp name "tusss"
Welcome, ${dragonfly_data[name]}.

BTW, if you are using *script for this stuff, you may as well use real Javascript arrays instead of your emulation (which, again, is really clever).

1 Like

Wow, this looks really interesting! What do you mean by it would be virtually unpublishable - are you saying that it wouldn’t be able to be used in a published HG/CoG?

How do you mean? I assume you can’t reference array values with ChoiceScript so you still need an I/O that uses ChoiceScript variables.

As far as I know, HG/CoG doesn’t support the use of *script, especially in the sense offering technical support. In order to publish, they have to be capable of offering technical support (not to mention this particular script relies on 3rd party servers). There might also be security reasons for them to not want to host games that use *script

This is what I had in mind. You could have a command

PersistData “varname”

varname is then added to a Javascript list your library maintains. Every time you send/get, you send/update all the variables in the list with a Javascript loop, so that the user doesn’t have to worry about packing/unpacking manually.

PS. I’d love to get an API key and, if possible, to see the server-side code too (private message?).

1 Like