Client/Server functions
From CryWiki
| ||||||||||||||
Contents |
Introduction
When we want to script in multiplayer, things get more complex. In singleplayer we do not have to care about connecting clients mid-game, disconnects, client/server achitecture and all that.
But once you want to get into the multiplayer side, everything needs to be considered. Don't run away yet though, the Crysis lua system is quite flexible. This article will explain how the client/server architecture works in Crysis (lua) and how you can make your own functions execute from server to client or the other way around.
The door
To start things of we'll have a look at an entity which is already present in Crysis: the door. The file is located at Scripts/Entities/Doors/Door.lua
Net Expose
The first network-related code we see is the Net.Expose function (with a table as argument).
|
| |
|---|---|
| |
The first thing in the table is the Class. The class is defined at the top of the file:
|
| |
|---|---|
| |
Keep that in mind when making your own entity multiplayer-compatible. Call Net.Expose AFTER defining your class.
After that, we can see the Client and Server methods
|
| |
|---|---|
| |
In here we define what functions can get called over the network.
- ClientMethods
- These functions are executed on the client, requested by the server.
- ServerMethods
- The functions in this table will be executed on the server, requested by the client.
Now let's take a closer look at what info the Net.Expose function needs for these functions.
FunctionName = { RELIABILITY, ATTACHMENT, FUNCTIONARGS },
- Function name
The first thing it needs is a function name. The unwritten rule is to prefix client-functions with Cl and server-functions with Sv.
- Reliability Type
- RELIABLE_ORDERED
- Ensures the packets are delivered, and in the correct order.
- RELIABLE_UNORDERED
- Ensures the packets are delivered, but with the possibility of receiving them in a different order.
- UNRELIABLE_ORDERED
- Does not ensure that the packets are delivered, but makes sure they're in the correct order if they are.
|
The most used one is RELIABLE_UNORDERED. This will work just fine for most functions. If you're repeatidly sending the same function and it needs to arrive in order, use RELIABLE_ORDERED.
- Attachment Type
- NO_ATTACH
- POST_ATTACH
- PRE_ATTACH
- Function argument types
The last thing to do is listing the function arguments that need to be passed on the network. Possibilities are:
- BOOL
- A boolean value (true/false)
- STRING
- A string
- INT8
- An integer of 8 bits
- INT32
- An integer of 32 bits
- VEC3
- A vector table (with x, y and z values)
- ENTITYID
- An entityId value
- FLOAT
- A floating point value
- STRINGTABLE
- A table with strings
- DEPENTITYID
- ???
There are probably more around, but those are the ones that are used the most.
Just keep this in mind:
|
Function implementations
Let's have a look at the actual functions now. How do they work and how do you call them?
A server function: SvRequestOpen
|
| |
|---|---|
| |
The first thing to note is the .Server just behind the ClassName. The Server part is a table in the Door class (table) generated by the Crysis Lua (Network) system.
The function has to be in the Server table because we said it would be a server function in Net.Expose.
|
| |
|---|---|
| |
From this info, we know the first argument will be an entityid, and the second will be a boolean. If we go deeper inside the function we can see it sets the door to open or closed, depending on the argument, and then calls the Open() function. Let's take a look at that.
|
| |
|---|---|
| |
A lot of code here, but we will just look at the essentials. The function first sets the correct action (door state) and returns if it was the same as last time (ie: opening a door twice). Then it checks if there's a Range value in the Door.Properties.Rotation table. If that's the case, we have a rotating door. Next, it calls the Rotate() function, which will actually do the rotation (We haven't done any rotating so far).
Finally we send the new door status to all clients (the door has to open/close), so every connected client can see the door opening, with
|
| |
|---|---|
| |
This is how we execute a function from the server -> client. We want all clients to execute the ClRotate function with the supplied arguments.
Instead of sending it to every client, there are also ways to limit the number of clients to send it to.
- To all clients
|
| |
|---|---|
| |
- To all clients, except 1
|
| |
|---|---|
| |
- To 1 client
|
| |
|---|---|
| |
|
To send a function to only 1 specific team, you'll have to loop through all players and check their team, and then send a command to them individually, using the method above.
It's time to look at the ClRotate function now.
A client function: ClRotate
|
| |
|---|---|
| |
As with the server function, we can see what the arguments will bring in Net.Expose: 2 booleans. The function itself is pretty simple, first we check if we are not a server (this is a common thing to do, we do not want the code to be executed twice on it) or if we are in demoplayback-mode. Then the action is set and self:Rotate() is called.
In the Door:Rotate() function we do the actual turning of the door, which is not in the scope of this article.
The final thing you might wonder is: When and how has initiated the SvRequestOpen function? A search in the file leads us to the Door:OnUsed() function.
|
| |
|---|---|
| |
The OnUsed function gets called by the lua system when using an entity (in this case, the door). So when a client (not necessarily you) uses a door, he will send a request to open the door, to the server.
You can see the syntax for client->server is even more simple than server->client. We just have to add a .server part and we're good to go.
|
Schematic form
Handling connecting clients
The final thing we have to consider is clients connecting in the middle of the game. Say client A opens a door. After a while client B connects. If client B runs up to the door he will see a closed door, since he didn't get the 'open' message from the server, which was sent at the time client A opened the door.
To solve this there's a function which gets called when a client connects: OnInitClient.
|
| |
|---|---|
| |
When a client connects, this function gets called. It just sends the current state of the door to the client. So if client B now runs up to the door, he should see it's open.

