Ismoh / NoitaMP

WIP! Not working, atm! NoitaMP, Noita Multiplayer: First synchronous multiplayer mod for Noita!
GNU General Public License v3.0
117 stars 12 forks source link

Developer needs help on RE - Trigger entity spawning #67

Open Ismoh opened 2 years ago

Ismoh commented 2 years ago

Might be that I could use some RE help. I never was depening on RE help so much.

The problem I have is: My concept is that the server holds the truth. Spawning, moving entities, everything. Thats why I remove all entities on client side. BUT... There is no Noita API function (or I dont know a specific one), which triggers spawning entities, therefore the client wont see any entitites spawning, when not following the servers player. I have no idea where to look in ghidra or even how to use it or if there is any Noita API function or a specific combination of components, which are needed.

I think I am going to create a 'wanted help' issue on github, because if this isnt fixed, multiplayer would mean to follow the servers player all the time and this sycks.

If I wont remove entities on client side, everything would be multiplied by amount of clients, which is even more sycking!

Anyone out there you can help me?

I still want to have a look on RegisterSpawnFunction, because I dont know it or used it before.

I really hope there will be a solution, because it's a big game changer!

Ismoh commented 1 year ago

Based on the default player_base.xml I created in the past a client_player_base.xml to see a copy of clients minä. I commented out everything not need by best guess. As @GrandpaGameHacker mentioned, after RE Noita, it might be enough to add a CameraBoundComponent (double check wording) to make the clients minä copy trigger spawning entities on server instance to be synced on all clients. This is needed, because entities on client will be removed by default, otherwise everything would be duplicated and not in sync.

ofoxsmith commented 1 year ago

PlatformShooterPlayerComponent controls which entity the camera follows and where it is positioned, and the GameGetCameraPos, GameSetCameraPos, GameSetCameraFree allow for the camera to be moved with lua, but I don't think they will be useful for what we need to do.

ofoxsmith commented 1 year ago

I assume there can only be one entity with a PlatformShooterPlayerComponent in the world, as the camera can't be centred on two entities.

Ismoh commented 1 year ago

How about adding the component and setting center_camera_on_this_entity and move_camera_with_aim to 0? I think, when the local minä dies, the camera might move to the other players, when setting those values to 1 after minä died. Would be a nice feature?!

Ismoh commented 1 year ago

As discussed with @ofoxsmith we decided to implement a different approach. We're going to have a serialisation and compression of a whole entity within his children and all components, which then is shot over network to server. Clients eentity will be destroyed. Server then sends this chunk of squeezed entity to all other clients, which then spawns the entity, if it is in range (camera bounds) There will be pitfalls like:

Ismoh commented 1 year ago

@ofoxsmith I created a branch based on develop!

Ismoh commented 1 year ago

https://discord.com/channels/453998283174576133/632303734877192192/1076866773850275922

Ismoh commented 1 year ago

I thought about, how to implement syncing rootEntities and all below, like children and components. This is my result: Pseudo code:

local entitiesInRadius = EntityGetInRadius(pos_x, pos_y, radius) or {}
for entitiesInRadius do
 if entityInRadius[i] == EntityGetRootEntity(entity_id) then
-- get all attributes of root entity
-- todo
-- get all components of root 
local rootComponents = EntityGetAllComponents(entityInRadius[i]) or {}
for rootComponents do
-- store comp type, name and value into a string, table or json
-- I am pretty sure we need a lookup table like: if component type == VariableStorageComponent then field_name = "name" etc pp
end
-- get all child entities
local children = EntityGetAllChildren(entityInRadius[i]) or {}
for children do
-- recursive getAllComponents and store in string, table or json
end
else
--skip children
end

So we need to get the root entity, but ignore children. If entity == root, (1)then get all attributes and store those, then iter all components and store type, field_name and value, then get all children and repeat (1).

Ismoh commented 1 year ago

[17:08]stefnotch: If we want to clearly communicate that to the players: Would it make sense to have an indicator that appears when you're further away from the host. As if to say "you're on your own, things that happen here are closer to singleplayer than multiplayer"?

Ismoh commented 1 year ago

If server is in range ask for spawn, if server isn't in range ask only for nuid. Check for entityData if other minäs are in range, if so send otherwise not. If anything comes in range, make sure to spawn the entity: think of an projectile flying millions of pixels!

Ismoh commented 1 year ago

Make sure all sendToPeer are put into functions. In addition do checks that parameters aren't empty. Global function in MinaUtils for distance between minas.

if any mina in range then
  send serialized enitity to server
  Server send to all clients
end
Every peer checks, if entity is in range
then spawn, if not ack only
Else if no mina in range, do nothing
Ismoh commented 1 year ago

Make sure entities are send immediatly, when spawned, no matter what tick is set, otherwise it's getting out of sync. Send entity data only on tick!

Ismoh commented 1 year ago

https://discord.com/channels/453998283174576133/632303734877192192/1082309565900668978

Ismoh commented 1 year ago

Add a list of entity names, which only be loaded once, like bosses. Can be modified by modders.

Ismoh commented 1 year ago

If client is in server minä range, remove entities.

Ismoh commented 1 year ago

Dextercd np.SetGameModeDeterministic(true). This basically makes the game think it's a daily run. It makes it so all spells are available during the run and using spells does not count towards spell progress.

Ismoh commented 1 year ago

When needNuid is sent, serialise the entity and send it to the server. Remove then on client. Server will add nuid and sent spawned entity to the client.

Ismoh commented 1 year ago
Ismoh commented 1 year ago
Ismoh commented 1 year ago
Ismoh commented 1 year ago