How to pass Colyseus room to next collection?

Hi all,

I’m one day new to Defold so this may be pretty simple. My setup is this:

  • main.collection: This just contains a loader that, on init, loads up splash.collection and then otherwise just listens for messages to unload one collection and load another.
  • splash.collection: This is like a main menu. Here I have a “Create Game” button which creates a room via Colyseus.
  • level.collection: After pressing “Create Game” we should go here.

My problem is I cannot figure out how to get the room from splash (where I make it) to level.

Approaches I have tried:

  • Message passing: This does not work because room is a function.
  • Lua modules: I have tried every one of the options here. The metatables and closure ones don’t work to save the room to the module state at all, whereas the first option (under the stateful section) does work to save it to the module but then I can’t read it from level.collection, it is just nil.

Another option that I see in this tic-tac-toe example is to just have all the game logic in the top level collection, which means I don’t need to ever send the room anywhere. I don’t love this approach for anything more complex than tic-tac-toe, though maybe this is the best way

What I imagine I need is to get the room into some place (a Game Object perhaps) where:

  • I can send it messages to send onwards to the server.
  • It can send my components messages to update their state based on state changes from the server.

Any help would be much appreciated both in regards to how to pass the room into another collection as well as more general architectural advice for what I’m trying to do.

Thanks!

In case it helps, here is most of the relevant code: defold question code.zip (11.1 KB)

Yep, as you said, message passing doesn’t work for functions.

This is usually the way to go if you wish to share state and functions between scripts. You should go back and revisit this solution. The absolute basics for a Lua module:

-- mymodule.lua
local M = {}

local message = "Hello world!"

function M.hello()
    print(message)
end

return M

Each time you call require("mymodule") you will get back the same Lua table M. What this means is that if you require the module in splash.collection and update something in the module those changes will remain when you require the module from the level.collection.

I took another crack and it and I figured it out. It seems like the docs aren’t quite right.

This is what I do:

-- room.lua
local M = {}

function M.set_room(the_state, v)
	the_state.value = v
end

function M.get_room(the_state)
	return the_state.value
end

function M.new(v)
	local state = {
		value = v
	}
	return state
end

return M
-- splash.gui_script
local room_module = require "room"
local room_module_state = room_module.new(nil)

-- in create_game, assume I have a non-nil _room
room_module:set_room(_room)

-- in on_input
if room_module:get_room() then
    -- send message to go to level
end
-- level.script
local room_module = require "room"
local room = room_module:get_room()

-- I have confirmed that room is non-nil at this point.

This all works perfectly. In the docs however, it says you should do m.alter_state and m.get_state, whereas I think it should be m:alter_state and m:get_state, as in the above code.

Does that sound right?

If so I can open a PR to fix up the docs.

Hmm, I need to take a closer look at the code in the documentation, but it will have to wait until tomorrow.

Do note that the colon is just a fancy way of passing the object itself as the first argument. These two are equivalent:

local greet = "Hello %s"
print(greet:format("bob")) -- Hello bob
print(string.format(greet, "bob") -- Hello bob

Neato! All new to me, this is my first time coding lua.

Thanks for the help, let me know if you want me to make the change.

To be honest with you banool, for personal clarity’s sake I would recommend avoiding passing the room, and just have a single lua module that handles all of your colyseus messages. That’s what I have been doing so far.

1 Like

Thanks, sounds like a good call!

I don’t suppose you have a Defold + Colyseus project I can look at? I’m having issues with the Colyseus state. Imagine I have a state like this:

class Position extends Schema {
    @type("number")
    x: number;

    @type("number")
    y: number;
}

class Player extends Schema {
    @type(Position)
    position: Position;
}

export class MyLobbyRoomState extends Schema {
  @type({map: Player})
  players = new MapSchema<Player>();
}

When I read the state, I can go down as deep as position, but position.x and position.y are nil. As in, room.state.players[room.sessionId].position.x is nil, even though I can see on the server that it is not.

Looking at a large example (larger than those I can see in the Defold / Colyseus repos) would be helpful.

Unfortunately our repo is private and I can’t make it not private.

My questions would be:

  • How long after connecting to the server are you trying to access room? It can take a few seconds.
  • Try initialising x and y with a value, either in the class or after they are created in the player class
  • Try a function to react to the position change like:
room.state['on_change'] = function (changes)
    for i, change in ipairs(changes) do
        print(change.field)
        print(change.value)
        print(change.previousValue)
    end
end

This should allow you to tell that the change is happening.

1 Like

Thanks for the tips, thanks!

1 Like