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!
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!
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+ Item
s. 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!
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!
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!
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:
splash.gui_script
to run.room.lua
.splash.gui_script
sends a message to loader.script
to tell it to unload the splash and load the level.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:
ERROR:WEBSOCKET: Failed to setup callback
. Currently it is very opaque, it doesn't tell me what specifically failed in setting up the callback.Any help would be very much appreciated!