Core Context

CORE CONTEXTS
Estimated Completion Time: 20 minutes
Core Version: 2.2.33

SUGGESTED PREREQUISITES:
Contexts - Core Documentation
Networking in Core - Core Documentation

TUTORIAL SUMMARY:

  • When creating multiplayer games, it is important to allocate resources properly. Optimization will be one of the most important indicators for a positive player experience. Topic of the tutorial is contexts in Core. Contexts are closely intertwined with optimization and network performance, as well as security (anti - hacking). Proper use of contexts can make the game smoother, more responsive and richer in gameplay (because more resources will be available)*

EXPECT TO LEARN:

  • Understanding contexts
  • Basic networking
  • Basic security
  • Basic optimization

TUTORIAL:
Any game consists of objects (level objects, scripts, environment objects, lighting, effects, GUI, etc.). Some we need all game time, when others need to be spammed and/or deleted in real time). Some require collision to be present (so that players and other objects can collide with them) while others have only a graphical shell. Some need to be transmitted over the network and synchronized for all players, while others are left unchanged throughout their lifecycle. We also need to specify the location of the object (client, server or client + server). All of these features have a different load on the engine and the network. For example, a large number of objects that have to be synchronized will lead to lags and packet loss.

The differences between these objects give rise to the need for contexts.. Each of which gives the object its own properties and capabilities, while also imposing a number of constraints on their use.

In core we have:

  • Default
  • Networked
  • Server
  • Client
  • Static

DEFAULT CONTEXT

The context that objects get by default. Once an object is in the hierarchy it will be in the default context. All objects will exist on server and client.

Great for level objects that must have collision and won't change during gameplay (e.g. the floor the player will walk on).

This is the only context in which you can't create or delete objects in real time.

Also, if your objects don't have any specific roles (they need to move, change their parameters etc.), you can leave them in the default context.

Scripts in static contexts don't differ from server contexts, but there is one small difference. When the player enters the game, client scripts, scripts in static context and scripts in default context will be downloaded on his PC. Particularly curious players will be able to see the contents of these scripts. So if you want to hide the contents of the executable code on the server then place the scripts in the server context

DEFAULT (NETWORKED) CONTEXT

To make a network object, right-click on it and enable the corresponding option.

These objects as well as the default context exist on the server and on the client. But any server changes of the object (location, color, rotation angle and any other parameters) will be synchronized for all players. This is the easiest and fastest way to create objects that will be synchronized over the network. This context is great for weapons, pickups, ai enemies, moving platforms, etc. Also if the custom properties of those objects will be with enabled "Dynamic property" option) Right click on custom properties and enable that option. Then these custom properties will also be synchronized on the network.

But you should always remember that any change of network parameters will send all information about objects (including all properties) to all players on the server. This may create a huge load on the network. At the moment there is a restriction in core on the number of network objects

and the maximum allowed value is 4000

Your current number of objects is calculated as follows: numberOfNetworkedObjects * maximumlNumberOfPlayersOnTheServer

I advise sticking to this limit and keep in mind that everything depends on refresh rate hence network transmission rate. Important detail is that the player will have to download all networked objects and all information about them (including data stored in custom property). Often it is because of excessive or improper use of this context that causes game lag. And network code optimization is usually the most difficult. But there are techniques that we will look at in the second part of the tutorial.

Only server scripts can interact (Change properties) with these objects.

A network context can contain other contexts (client, server, static), this can be used as a good optimization technique. For example, you have a weapon which model consists of many objects, The model is just a graphical shell, we can put it in the client content to greatly reducing the required resources.

The "Geo" group contains all objects related to the visual view of the weapon

CLIENT CONTEXT

To place objects in a client context you need to create it by clicking the right mouse click and selecting the appropriate option

Next, place the objects in the group that appears.

The objects in the client context will only exist on the players' devices.

Great for displaying effects, GUI, playing sounds, cosmetic objects with animations (for example, a mill with a spinning propeller in the background because we do not care about propeller collision and we don't need to sync the spin for all players - this can be implemented in the client context)

Objects in client context cannot have collisions, except for triggers. It's important to remember that client scripts exist for each player separately. You can both synchronize the behavior of game logic (default behavior) and you can also specify events for the player on whose device the script is running on. Such a player is usually called a local player.

The code will display the name of any player entering the trigger

local TRIGGER = script.parent

local function OnBeginOverlap(trigger, object)
  if object:IsA("Player") then
    print("Player entered trigger", object)
  end
end

TRIGGER.beginOverlapEvent:Connect(OnBeginOverlap)

But we can modify the code to receive notifications only about the local player

local TRIGGER = script.parent
local LOCAL_PLAYER = Game.GetLocalPlayer()

local function OnBeginOverlap(trigger, object)
  if object:IsA("Player") and object == LOCAL_PLAYER then
    print("Player entered trigger", object)
  end
end

TRIGGER.beginOverlapEvent:Connect(OnBeginOverlap)

The client context has vulnerabilities to cheating, which we will talk about in the next chapter

SERVER CONTEXT

This is mainly intended for scripts that implement server logic. All your "State" of the game should be formed here (e.g. round management, saving and loading player data, etc.)

Objects in server context can’t have collision.

Also server scripts can change parameters of network objects.

There is no vulnerability to cheating unless you create one yourself (more in next section).

Placing objects in a server context is almost never needed or useful. The only example i can think of is

I can give you only one example of a task where placing objects in server contexts can be useful: building routes from primitives for further use in scripts. Like a walking path of an enemy NPC in Tower Defence games.

STATIC CONTEXT

This is the most difficult context to understand. Objects in this context exist both on the client and on the server (this also applies to scripts). The object's behavior is similar to the default context, with the difference that objects can be spawned in and/or deleted in real time.

I suggest using it if you have enough Core development experience. Suitable for creating static (fixed) collision in real time. Procedural generation of levels.

An example of use will be discussed in the next section.

SCRIPTS CONTEXT AND SECURITY

This part comes with the comunity project.

Scripts you create can be placed in different contexts. Instances of the same script can be placed in different contexts. Let's take a look at the ninja weapon store example

Working Principle:

there is a database of all items in the store

the player has a client GUI that displays all of the items in the database

each item in the store has a Buy button, and pressing it sends an event to the server to let the player know his intent to buy

the server listens to this event and upon receiving it performs a validation of the data (whether the item with the same ID exists in the database, whether the player has enough money for it and only then does the purchase)

note. This example does not consider the issuance of purchased items, as it is not relevant to the topic of the lesson.

Hierarchy of the resulting system:

Example of item in Database

The Products group acts as a database of goods in our store. We need the data on the products both on the server (for charging the player and issuing specific items later) and on the client (for displaying the information on the product in the store).

To be able to use this data in the scripts I created the script

and by selecting it in Project Content (without putting it on the scene) I added to it the Products property (reference to our database)

Dragged the group from the hierarchy

The code in the script goes through all of the objects in the Products group and adds to the table all custom properties that are on the object (in our case it is Name, Icons, Price)

local PRODUCTS = script:GetCustomProperty("Products"):WaitForObject()

local products = {}

for key, product in pairs(PRODUCTS:GetChildren()) do

table.insert(products, product:GetCustomProperties())

end

return products

Now we can use the database in any of our scripts (both on the server and the client)

local Database = require(script:GetCustomProperty("ShopDatabase"))

In the Shop_Client script

We create a view for each item in the database, fill it with information and subscribe to the pressed event of the buy button.

We will use this event to send a purchase request to the server.

A common mistake game developers make is to send insecure data to the server:

local price = 1000;

Events.BroadcastToServer("Buy", "Milk", price )

In this case, the cheater can replace the value of the variable price with -1000 and he gets the money for buying the item in the store =)

The safest and most correct way is to send the IDi of the item the player wants to buy and do the validation on the server

-- Client script
Events.BroadcastToServer("Buy", itemId)
-- Server script
local function OnTryBuy(player, itemId)
  local item = Database[itemId]
  print("Player try buy", player.name, itemId, item.Name, item.Price)
  local money = player:GetResource("Money")

  if money >= item.Price then
    player:RemoveResource("Money", item.Price)
  end
end

Events.ConnectForPlayer("Buy", OnTryBuy)

For example, your task is to create a bridge of cubes that collapses when you stand on it.

If you implement this with a networked object (each part of the bridge is a separate networked object, then with only a few bridges we will reach the networked object limit)

one part of the bridge consists of a mesh and a trigger

We need the trigger to determine the moment when we need to destroy a part of the bridge.

That's why we will use static context techniques.

In the picture you can see the hierarchy of the system

Our data channel is a "Data Object" with enabled dynamic property option

Each of our cubes has two states :

  • exists (we will use number 1 for iit)

  • does not exist (we will use number 0 for it)

For example, if our bridge has 10 parts and all of them are integers, we can express it by the string

"1111111111"

And if the player breaks the first two parts, the data will look like this:

"0011111111"

In the static context we put a StaticBuilder script, which has a link to the Data Object. In it, we listen to the event of change of customProperty of Data Object

The OnCustomPropertyChanged function

When data changes on the object, it rebuilds the bridge depending on the current data (the code is far from perfect and serves only for demonstration of the system work - this is not the most optimized solution)

Since the script is in a static context it means it exists on the client and server and the code responsible for building and destroying parts of the bridge will be called on both the client and server, which will provide the correct collision.

I will be happy to get your comments. Especially critical :slight_smile:

2 Likes