-
Notifications
You must be signed in to change notification settings - Fork 1
UsersModule
This article describes the various user-related things that are implemented in the Snipe server core. The user record is stored in the database and loaded as a block when the client logs in with this user's credentials. The block contents are transferred to slave server that the client has logged in to. The snipe.slave.data.UserCore
class instance is initialized with all subclasses (or in case if it was extended, the project-specific user class and subclasses). The link to this user instance is then stored in the client instance. The user class also contains a link to user quests and chains class instances. These are initialized from the separate user quests block and described in the Quests module article.
The editor can be used to search for the user record and edit it in real-time if necessary. The requests will be routed through the cache server to the slave server that this user is currently on.
If you want to directly operate on another user in the game server (whether on a different server, or simply a user different from the one that is bound to current client), you will need to use server notifications mechanism described in the Server Notifications article.
If you're extending the user functionality with more sub-classes, take a look at the Generic Containers article.
The basics of user registration and the login process is described in the User Registration and Login article. This section is focused on extending the basic mechanism.
By default the user record contains nothing, no attributes, no variables and no inventory. To modify the initial contents of user record you can use the editor "Proto-user" link located in the bottom menu of the main page. It leads to the same user edit page as in the case of any real user, but the contents that you put into the proto-user will be copied over on the user registration to the new record.
Extending the registration and login is done through various cache server and slave server subscriptions. The relevant cache server subscriptions (provided by the snipe.cache.ModuleCache
class) are:
-
user.registerModify
enables the modifying of new user parameters on user registration. -
user.registerPost
is called after the user registration, and can be used to do some work on cache server. -
user.loginPost
is called after the user successfully logs in to game server.
Slave server subscriptions (provided by the snipe.slave.Module
class):
-
user.registerPre
can be used to disallow user registration in specific cases. -
user.register
is called after all user registration checks before the actual user registration logic. -
user.loginPre
is called before the user logs in to game server, and can be used to disallow user login.
Server subscriptions are described in detail in the Module Subscriptions article.
User attributes are a generic container of key-value pairs that are stored in the user block. You can edit attributes of any user on the user edit page of the editor (search for the user SNS ID or name using the form on top of the editor and click "edit" link in the search results). To edit the user attributes you will need to define user attribute types first. The "User Attribute Types" link is located in the bottom menu on the main page. You don't have to define any user attribute types to use them on the server. For example, you can certainly leave out the internal user attributes that are not intended to be sent out to the client.
On the game server side, user attributes can be accessed through the API in snipe.slave.data.UserAttributesCore
class. There are methods to get, set, increase and decrease attributes that you can read about in the API documentation. The class also provides two useful hooks: setPre()
and setPost()
. These can be used to prevent the setting of the attribute or do some work after the attribute has been set, respectively. To gain access to these you will need to extend the base user attributes class.
Here is how to do it. First, create a class that extends the base class:
import snipe.slave.data.UserAttributesCore;
class UserAttributesTest extends UserAttributesCore
{
public function new(s: ServerClass, u: UserTest)
{
super(s, u);
}
override function setPost(key: String, oldValue: Dynamic, value: Dynamic): Bool
{
// call reputation hook
if (key == "reputation" && value > oldValue)
user.reputationUpdated(oldValue, value);
}
}
The example here calls a specific hook that does something when user reputation is updated.
Now that the class is ready, you need to do some things to set the server up. First thing you need to do is modify the user class itself or create a new one if you haven't done this already:
import snipe.slave.Block;
import snipe.slave.data.*;
class UserTest extends UserCore<UserAttributesTest, UserVariablesCore,
UserQuestsCore, UserChainsCore,
InventoryCore, EffectsCore, BadgesCore>
{
public function new(s: ServerTest, b: Block, l: String, classes: _UserClassesList)
{
super(s, b, l, classes);
state = '';
}
public function reputationUpdated(oldValue: Int, value: Int)
{
attrs.set("rank", 1 + Std.int(value / 1000));
}
}
The user class has all of the sub-classes names in it as parameters. The default user uses the classes from the core. In this example we switched the UserAttributesCore
to UserAttributesTest
, our new class.
The next thing to do is open up the game server class file and set up the link to the user sub-class in the constructor like this:
class ServerTest extends ServerGame
{
function new(metav: MetaServer, idv: Int)
{
super(metav, idv);
serverType = 'game';
coreModuleParams.user.enabled = true;
coreModuleParams.user.classes.userAttributesClass = data.UserAttributesTest;
}
static function main()
{
var meta = new MetaServer('game', ServerTest, server.Client);
meta.initServer();
meta.start();
}
}
You can see the full list of field names in snipe.slave.data._UserClassesList
type definition.
There is one more thing to do if you've just created the UserTest class and that is to fix the UserClass
type definition. Open up the UserClass.hx
that should be located in the class path of your project and fix it:
typedef UserClass = data.UserTest;
If you've followed the First Steps tutorial or started your project using its files as a base, you should already have that file.
Since the procedure is largely the same for all user subclasses (change user class definition, add the new class to game server class constructor), it will not be repeated in the sections below.
User variables have the same implementation as user attributes. They use the class snipe.slave.data.UserVariablesCore
instead. The idea to split attributes and variables into separate classes comes from the desire to have logical grouping. The attributes change more often, maybe with each client request while the variables change once per logical gameplay block or even more rarely. You can safely ignore the variables if you want, there are no consequences for storing everything about the user in user attributes.
The user inventory is a container for in-game items. Each inventory item has a numerical item ID and amount. The inventory class snipe.slave.data.InventoryCore
has an API for doing everything you might want to do:
-
add()
- Adds item to inventory with a given amount. -
decrease()
- Decreases item amount by 1. -
dump()
- Dumps inventory into an object to send to the client. -
get()
- Returns item by its ID. -
getAmount()
- Returns item amount by its ID. -
getByStringID()
- Returns item by its string ID. -
has()
- Returns true, if the inventory contains this item. -
hasStringID()
- Returns true, if the inventory contains an item with this string ID. -
put()
- Puts an item in inventory with a given amount. -
remove()
- Removes an item from inventory. -
removeAmount()
- Removes that amount of given item.
The badge (also called achievement) is a structure representing some goal that the player can accomplish in your game. You can use the editor to create and modify badges (the link is in the bottom menu of the main page). The intended flow here is that the player tracks the badge progress through the use of special quests that each give out a badge level when completed. The quest chain is marked as containing badges on creation, the player receives all quests from this chain at some point and when the quest completes, he receives the specified level of the badge. You can, however, ignore that flow and use the low-level snipe.slave.data.BadgesCore.set()
method to give badges to players in some other way.
The user effects represent the timed buffs, debuffs, curses, etc that are applied to the user at some point and fade away when the specified period of time passes. Each effect has an ID, and time left until it disappears. The server checks all the effects on the user every two seconds using the client timer. This means that the time when effect finishes is inaccurate. If the client does not send any messages, the effect timeout will be delayed on the server until it does. If you require more precise effects you can implement a server timer that calls the effects check once per second for every client on the server.
To create an effect in the editor you need to create an effect attribute types first just like with item attributes or user attributes. Each effect is a collection of effect attributes that have a type and a value attached. "Effect Attribute Types" link is located in the bottom menu on the main page. After the attribute types are ready you can add a new effect ("Effects" link in the bottom menu on the main page) and set up some attributes for it. Note that string ID is important here. It will be used in the server code.
The user class on the slave server has a snipe.slave.data.EffectsCore
sub-class to operate on user effects. Here's the list of available methods:
-
add()
- Adds new effect or sets the time for an old effect. -
finishPost()
- Hook that is called after any effect finishes working. -
getAttributeSum()
- Returns sum of all effect attribute values of this type. -
getRemainingTime()
- Returns remaining time for an effect by ID in seconds. -
getTime()
- Returns full time for an effect by ID in seconds. -
hasEffects()
- Returns true, if user has any effects. -
hasID()
- Returns true, if user has effect with this ID. -
hasStringID()
- Returns true, if user has effect with this string ID.