I created a websocket server and client plug-in!

I finished my first native plug-in for Solar2d last night! It allows you to create a websocket server in your Lua code on iOS, Android, TVOS, Mac, and their respective simulators. It even works in the Corona simulator. HTML5 is obviously not supported because you can host a server in a browser. Windows (builds and simulator) is not yet supported because I don’t have a Windows computer on me at the moment, and I might have to find another c library to make it happen (my current implementation depends on unix-styled APIs).

Using it is simple. Here’s an example of a server and a client implementation on the same device.

local SolarWebSockets = require "plugin.solarwebsockets"

local message = display.newText("debug messages here", 120, 320, nil, 6)

local clients = {}
local json = require("json")
local function wsListener( event )
    message.text = json.encode(event)
    if event.isServer then
        if event.name == "join" then
            print("join", "clients #", #event.clients)
            print(event.clientId)
            print(event.clientIp)
            clients = event.clients
            for i=1, #event.clients do
                print(
                    "client "..tostring(i),
                    tostring(event.clients[i]),
                    tostring(event.clients[i].clientId),
                    tostring(event.clients[i].clientIp)
                )
            end
        end
        if event.name == "message" then
            print("got message")
            print(event.clientId)
            print(event.clientIp)
            print(event.message)
            SolarWebSockets.sendClient(event.clientId,"echo "..event.message)
            SolarWebSockets.sendAllClients("someone said "..event.message)
            if event.message == "kick" then
                local clientToKick = clients[1]
                if clientToKick then
                    print("going to kick "..tostring(clientToKick.clientId))
                    SolarWebSockets.kick(clientToKick.clientId)
                end
            end
        end
        if event.name == "leave" then
            print("leave","clients #", #event.clients)
            print(event.clientId)
            print(event.clientIp)
            clients = event.clients
            for i=1, #event.clients do
                print(
                    "client "..tostring(i),
                    tostring(event.clients[i]),
                    tostring(event.clients[i].clientId),
                    tostring(event.clients[i].clientIp)
                )
            end
        end
    elseif event.isClient then
        if event.name == "join" then
            -- connected
        end
        if event.name == "message" then
            print("got message from server")
            print(event.message)
            -- send reply
            -- SolarWebSockets.send("reply "..event.message)
        end
        if event.name == "leave" then
            -- not connected anymore
            print("left server")
            print("error code: ",tostring(event.errorCode))
            print("error message: ",tostring(event.errorMessage))
        end
    end
end

SolarWebSockets.init( wsListener )

local widget = require("widget")

widget.newButton({
   left = 50,
   width = 100,
   top = 50,
   label = "Start Server",
   shape = "rect",
   onRelease = function()
       SolarWebSockets.startServer()
   end,
   fillColor = { default={ 1, 1, 1 }, over={ .2, 0.2, 0.2 } }
})
SolarWebSockets.startServer()

widget.newButton({
    left = 50,
    width = 100,
    top = 150,
    label = "Kill Server",
    shape = "rect",
    onRelease = function()
        SolarWebSockets.killServer()
    end,
    fillColor = { default={ 1, 1, 1 }, over={ .2, 0.2, 0.2 } }
})

widget.newButton({
   left = 50,
   width = 100,
   top = 250,
   label = "Kick someone",
   shape = "rect",
   onRelease = function()
       local clientToKick = clients[1]
       if clientToKick then
           print("going to kick "..tostring(clientToKick.clientId))
           SolarWebSockets.kick(clientToKick.clientId)
       end
   end,
   fillColor = { default={ 1, 1, 1 }, over={ .2, 0.2, 0.2 } }
})


-- client
widget.newButton({
    left = 150,
    width = 100,
    top = 50,
    label = "Connect to Server",
    shape = "rect",
    onRelease = function()
        -- wss not supported on iOS...yet
        SolarWebSockets.connect("wss://echo.websocket.org")
    end,
    fillColor = { default={ 1, 1, 1 }, over={ .2, 0.2, 0.2 } }
 })
 
widget.newButton({
    left = 150,
    width = 100,
    top = 150,
    label = "SendMessage",
    shape = "rect",
    onRelease = function()
       SolarWebSockets.sendServer("sample message")
    end,
    fillColor = { default={ 1, 1, 1 }, over={ .2, 0.2, 0.2 } }
})
 
widget.newButton({    left = 150,
    width = 100,
    top = 250,
    label = "Disconnect",
    shape = "rect",
    onRelease = function()
       SolarWebSockets.disconnect()
    end,
    fillColor = { default={ 1, 1, 1 }, over={ .2, 0.2, 0.2 } }
})

And you just include it like this

plugins = {
	["plugin.solarwebsockets"] =
	{
		publisherId = "io.joehinkle",
	},
},

I’m using this for local multiplayer games. It’s really solid from my tests so far, as I’m using websocket server implementations in C and Java (for Android).

I’m going to open source this soon and see if I can get it on the Solar2D direction also. Hope you guys like it or find use.

10 Likes

Greate! I used it but what import command i need?

1 Like

It isn’t up yet–I just have it on my local machine. I’m going to work with Vlad to get I published today/tomorrow. I’ll let you know when it’s up.

2 Likes

It’s been fun watching you develop this and I’m excited to give it a whirl!

So looking forwards to seeing this. So much potential! :slight_smile:

Here’s the repo. I was working on it after midnight and got both client and servers working in in. Anyways leave it a star because it gives me dopamine.

And I’m working at this hour because I’m really sick, and slept for about 5-6 hours during the day. I felt amazing in the middle the night, so I knocked it out. Hope you guys enjoy. I’m going to sleep.

1 Like

And starred :slight_smile:

I’m gonna have a play with this in the coming weeks. I’ve gotta say, I’ve been waiting for a real socket server implementation on Solar2D (or Corona as was) for years!

Well done!

1 Like

I had as well. I think I went nuts trying a lua socket server that just wasn’t working, and then thought “okay let’s just solve this once and for all!”

1 Like

Hey guys, so I got the repo in Solar2d’s org up, but the GitHub Actions isn’t triggering yet. I’ll post once that is up and running.

Update: It’s working! I haven’t tested it in the Simulator yet though

Okay the plugin is working on Solar/Corona version 3600 and above. Just add:

plugins =
	{
		["plugin.solarwebsockets"] = {
            publisherId = "io.joehinkle",
        },
},

Also, there is currently a bug in the simulator version of the plugin. If you build the app though it works. I’m going to put a patch up sometime this weekend. It think it was just a name-change issue when I changed the name of the dylib.

Please test this and give me feedback! Also put up a PR if you find an issue.

:slight_smile:

Another update: that macOS bug was really easy, it’s fixed! Just waiting for this PR to be approved and it will automatically work in the simulator :slight_smile: https://github.com/solar2d/plugins.solar2d.com/pull/1

2 Likes

This is still an issue with web/html5 builds. It’s only possible to support a websocket client (server is actually impossible in the browser), but the client seems to be broken. It’s probably an issue in the way it’s packaged.

Update: web/html5 builds are now working fine in version v4. No changes needed on your end. It should just work

Hi Joe, I was wondering, does your websocket plugin work with large messages? I am talking about messages with sizes varying between 10.000 and 90.000 Bytes. I am using Develephants Websockets plugin right now and it used to work great, but now it seems that it came to its limits since I don’t seem to be able to send large messages using the plugin.

Keep up the great work! :slight_smile:

1 Like

I use a modified version of Develephants plugin to support HTML5 in my plugin actually! (I don’t use his Lua solution though). He is just making simple js calls to the browser’s WebSocket object.

I would also recommend not using Develephant’s version, because it’s using a very broken/unstable websocket implementation in Lua for device builds. His HTML5 variant is excellent though.

I don’t know how big messages can get with my plugin, and it would probably vary from device to device because I’m using different libraries to support each device. You could always break down your long messages into shorter ones to get it working :slight_smile: hope this helps

1 Like

Hi @joecoronasdk,
A very interesting topic! It will be very great if you modify the version for Windows … I will be glad to use your plugin :3
I found a plugin similar to yours:https://github.com/Overtorment/NoobHub

1 Like

I currently don’t have a Windows computer to build or test it, but it should be fairly straightforward to implement this. I would use open up the Xcode project for mac you can find in my repo and use that to find a similar solution for Windows. You could reuse a lot of the c code I’d imagine.

Here’s what you’ll see in that project.

image

I’ll be here to help if you need anything. But if I did add Windows support myself, it would be probably next year after I’ve finished up some other projects. I don’t have time to implement it myself right now.

2 Likes

Hey just a quick update :slight_smile:

I finally took the time to migrate from Develephants Websockets to yours and your Websockets work great on the simulator. Even when sending very big data.Though I still have to do tests on my Android and iOS devices.

Just a note for people with the issue I had before: When using Develephants Websockets I created a function that would split my messages into multiple smaller ones. This actually works but the connection time to send/receive the message data becomes longer and about 20-30% of the time I got a disconnect. I supposed I could reduce/eliminate the disconnect errors but it probably would mean an even longer connection time to send/get the data. So I guess Develephants plugin is ok as long as the data you are sending doesn’t get too big.

1 Like

What are these large messages you’re sending?

We use a game editor that we created with Corona/Solar2D. The editor saves all game data (tile data, actor data, transitions, battle data, encounter data…) into a Postgres database. To do this we use a nodejs server that receives the data from the game editor and saves it into our Postgres database. To send/get the data to/from the game editor we use Websockets. Most of the time the messages are rather small. But some messages when initiating the editor are rather large, like the map tile data which consists of 10 huge number arrays. For example, the tile data of our first map (first image) has a total size of 156,951 bytes.

Game map on Game Editor
Screenshot-2020-10-14-at-12-40-02

Map data on Postgres
Screenshot-2020-10-14-at-12-44-23

2 Likes

Yeah try it with really small messages, and if that doesn’t work, make a simple GET endpoint on your node js server that downloads the whole map.

Really cool btw!

@andre.m,

You might be able to reduce your transmissions by selectively doing Run Length Encoding/Decoding when it makes the transmission shorter.