Navigation

  • Recent
  • Tags
  • Users
  • Search
  • Login
Colyseus
  • Login
  • Search
  • Recent
  • Tags
  • Users

Documentation GitHub

We're migrating to GitHub Discussions. This forum does not accept new registrations since April 6, 2023.
  1. Home
  2. banool
  3. Posts
  • Profile
  • More
    • Continue chat with banool
    • Flag Profile
    • Following
    • Followers
    • Topics
    • Posts
    • Best
    • Groups

Posts made by banool

RE: Best way to represent inheritance and optionals together in Colyseus

Hi @endel,

Thanks that's great to hear! My client side is in lua (Defold) but I assume there is something I can check at runtime to differentiate them, I'll just take a look at the messages I receive.

Thanks!

posted in Questions & Help • 5 May 2021, 16:11
Best way to represent inheritance and optionals together in Colyseus

Hi all,

First, some code to explain what I want:

class Character extends Schema {
    @type([ Item ])
    items: new ArraySchema<Item>();;
}

Imagine we have this character class. I want a character to be able to hold 0+ Items. An item would ideally be a superclass, with some subclasses, like this:

class Item extends Schema {
}

class Bucket extends Item {
}

class Hose extends Item {
}

What is the recommended way to do this? The problem with this approach is I would need some kind of client side check to determine which kind of item I'm holding. I'm also not sure this is possible at all, since the docs say "You can't mix types inside arrays." (here). If this was Rust for example, I'd probably have a bunch of items that impl an Item trait.

Another thing I would reach for in another language is enums. In Rust (again) for example you can have enums that have different values for each enum variant. This would be good for this case, as well as for representing optionals.

How have you managed to do this kind of thing? Would you just have n properties, one for each type of item?

As a related point, imagine also I want it to be possible for it to just be 0 or 1. I see there are no optionals, how would I do this? Do you just use an ArraySchema for this?

Thanks!

posted in Questions & Help • 1 May 2021, 18:31
RE: [Defold] room.state.on_change does not get triggered on state change

Hi!

Thanks for the super quick response. I see different options in different places:

  • room.state.on_change
  • room.state["on_change"]
  • room:on("statechange")

I'll go with the latter from now on.

Good to know that the on change callbacks are not recursive. I can put up a PR to make this a bit clearer on the docs.

Thanks!

posted in Questions & Help • 22 Apr 2021, 00:47
[Defold] room.state.on_change does not get triggered on state change

Hi all,

I am seeing what seems to me to be inconsistent results when registering callbacks for room state changes.

Here I register a callback for base state changes, like in @endel's example here: https://github.com/endel/colyseus-tic-tac-toe/blob/master/defold/scripts/game_controller.script.

room.state['on_change'] = function (changes)
	for i, change in ipairs(changes) do
		print("base state change")
		print(change.field)
		print(change.value)
		print(change.previousValue)
	end
end

Here I register a callback for something more specific in the state:

room.state.players.on_add = function(player, sessionId)
	player.character.position.on_change = function(changes)
                local position = {}
		for i, change in ipairs(changes) do
			if change.field == "x" then
				position["x"] = change.value
			end
			if change.field == "y" then
				position["y"] = change.value
			end
		end
                print("new position", inspect(changes))
	end
end

I know that the state changes are successfully making it from one client, to the server, and back to the other client, because I see the player on one client move on the other (and also from the logs).

When one client moves, therefore updating the state, I see only messages from the more specific callbacks:

DEBUG:SCRIPT: new position	{
  x = 869.49731445313,
  y = 370.85791015625
}

However I expect to see not only that, but also a message like:

DEBUG:SCRIPT: base state change

Do I misunderstand how these callbacks work? I would assume that if I assign a callback to the root state, it would fire any time anything changes within that state (recursively).

Also, in the docs here (https://docs.colyseus.io/state/overview/) it says that binary patches of the state are sent to the client every 50ms. Is it the case that if there are no changes, it sends nothing, and therefore the callback doesn't fire?

One specific reason I want a generic callback is I want to estimate how often I receive updates from the client so I can lerp the other players' movements accurately, though I think having a base callback might be useful in other ways.

In short, I'm not quite sure how on_change works, and I can't really figure it out from the docs ("this event is triggered when the server updates its state", it doesn't really elaborate on which callbacks get called for which state).

Thanks everyone!

posted in Questions & Help • 22 Apr 2021, 00:32
[Defold] ERROR:WEBSOCKET: Failed to setup callback when player joins room

Hi all,

I have code that functionally looks like this:

splash.gui_script

-- When the user presses Create Game
function create_game()
    client:join_or_create("lobby", {}, function(err, _room)
        if err then
            print("Failed to create the lobby:", err)
            return
        end
        room_module:set_room(_room)
    end)
end

create_game()
r = room_module:get_room()
if r and r.state.players and r.state.players[r.sessionId] then
    msg.post("main:/loader", "load_level")
end

room.lua

local M = {}

function M.set_room(the_state, r)
    the_state.room = r
end

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

function M.add_callbacks(the_state)
    add_callbacks(the_state.room)
    print("Added room callbacks")
end

function M.new(r)
    local state = {
	room = r
    }
    return state
end

function add_callbacks(room)
    room.state.players.on_add = function(player, sessionId)
	print("new player", sessionId, player)
    end
end

loader.script

local function load_level(self)
    msg.post("#level", "load")
end

function on_message(self, message_id, message, sender)
    if message_id == hash("load_level") then
        unload_splash(self)
	load_level(self)
    end
end

level.script

local room_module = require "room"

function init(self)
    room_module:add_callbacks()
end

Functionally this is how it works:

  1. Load into main menu (splash).
  2. Press create game, which causes the code in splash.gui_script to run.
  3. The room object is created and stored globally in room.lua.
  4. splash.gui_script sends a message to loader.script to tell it to unload the splash and load the level.
  5. The level calls add_callbacks to add the callbacks to room. This ostensibly works correctly, because I see Added room callbacks after this point.

What I expect to happen is, after this point, if someone joins the game, I should see a message like this:

new player <session_id> <player>

However instead I see this:

ERROR:WEBSOCKET: Failed to setup callback

Importantly, I only see this error when the second player joins the room, not when I add the callbacks.

My schema pretty much looks like this:

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

Player itself just has 3 fields in it: x: number, y: number, and name: string.

As far as I can tell, everything looks fine in the backend. I have logging that shows the two players connect:

onCreate: Created room: KRafbAeo9
onJoin: Joined: KRafbAeo9
onJoin: Options: []
Connected clients: [ 'kQITKomRu' ]
onJoin: Joined: KRafbAeo9
onJoin: Options: []
Connected clients: [ 'kQITKomRu', 'CHaf_yUQm' ]

If I add the callbacks immediately in set_room in room.lua, the callback works at first (it gets called for my own player joining). Later however, when the second player joins, I see the same websocket error, instead of the callback being called for the second player, which is very strange.

Furthermore, even if I could get that approach to work, it is too early to register the callbacks because at that point, the level has not loaded yet, since I join the room first and then load the level. I also feel like this is still a race, since the state change may happen before I assign the callback. The current approach, where I add the callbacks later, has the same problem. It seems to imply that I have to process the initial state first before relying on the callbacks on state change. Instead, I would love most of all to have all the callbacks get called retroactively based on the initial state. For example, if there are 2 players when someone joins, the callbacks for state.players.on_add gets called twice, but only after I have assigned the callback.

So two questions:

  1. How do I get more details about this error, ERROR:WEBSOCKET: Failed to setup callback. Currently it is very opaque, it doesn't tell me what specifically failed in setting up the callback.
  2. What is the best way to get it that I only have to write callbacks for state changes, instead of that and an additional function that processes the initial state. When I look at examples like this or this by @endel they seem to just set the callbacks without doing any initial state processing, so I feel this must be possible.

Any help would be very much appreciated!

posted in Questions & Help • 18 Apr 2021, 19:13

© 2023 Endel Dreyer