According to the requirements of our Phase I project, I designed the data format in the database yesterday. The database uses redis. I regard it as a remote data structure storage device. It provides basic key-value storage functions without hierarchical tables. If two layers of structure are required, you can save a group in the value.
Hashes.
This is my first time using redis. I have no experience. However, similar facilities were implemented by themselves a few years ago, with little difference. Over the past few years, with the redis open-source project, we do not need to recreate the wheel. However, the mode is quite familiar. That is to say, redis is used based on my historical experience.
Phase I projects require simplicity and do not plan to split data into different data servers. However, we are prepared for future splitting requirements. In the future, data can be assigned to different locations based on the key prefix. For example, the account information is most likely to be independent because it has nothing to do with a specific game.
The user system uses email as the user name, but the unique identifier in the database is a uid. The user should be allowed to modify the login name (the user may change the email ). User identification is identified by ID. Therefore, the database should have the following keys:
- Account: Count ID
- Account: userlist set (ID)
- Account: Email: [email] ID
Here, the value corresponding to account: userlist is a set, which stores all existing user IDs. Used to traverse all users. This may not be used for the time being, and there may be problems when the number of users is large. However, you do not need to consider so many issues for the time being.
Account: Count is a counter that can be used to generate a unique ID.
Account: Email: [email] indicates the login name of each registered account. [Email] indicates the email address used for logon.
Here, the email may also contain the symbol ":". To avoid this problem, many emails are encoded. My solution is to encode characters other than the letter number @. _ into the form of % xx. Using Lua to do this is very simple:
local function _encode(str) return string.format("%%%02X",string.byte(str))endfunction emailEncode(str) return string.gsub(str,"([^%w_@.])",_encode)end
Of course, decoding is also very easy.
local function _decode(str) return string.char(tonumber(str,16))endfunction emailDecode(str) return string.gsub(str,"%%(%w%w)",_decode)end
Next, it is the data of each ID under the Account:
- Account: [ID]: Version Number
- Account: [ID]: email string
- Account: [ID]: password string // MD5 (password .. Salt)
- Account: [ID]: nickname string
- Account: [ID]: lastlogin hashes
- Account: [ID]: History List (string)
- Account: [ID]: available Enum (Open/locked/delete)
The password does not need to be saved as plain text. Any possible data leakage will cause loss to the user, and I do not want anyone to see the user's password. Therefore, the MD5 (password .. Salt) style is used.
Before MD5 calculation, add a salt suffix because the MD5 value of the text is also searchable by the database.
Lastlogin stores the user's last login information and uses a hashes table because the information will be further expanded in the future.
History stores all historical records of user login, and uses a string linked list record.
When a user deletes his/her account, he does not want to delete the data from the database, but only wants to mark it under available.
Considering that the data structure in the database may change, the version domain is added as the version ID.
I don't want various services to directly read and write this data, so I will write a separate authentication server for processing.
The certification server provides three services:
User Registration
User name and password authentication (used for web services connected to SSL)
User name and password challenge authentication (for client authentication service)
The following are basic scenario service data:
- Account: [ID]: avatars set (ID)
- Avatar: Count ID
- Avatar: [ID]: Version Number
- Avatar: [ID]: account ID
- Avatar: [ID]: Scene string
- Avatar: [ID]: available Enum (Open/delete)
- Avatar: [ID]: Data hashes
- Name string
- Figure string
- World: Scene hashes
- Scene: Count ID
- Scene: [ID]: Name string
- Scene: [ID]: available Enum (open/close/delete)
- Scene: [ID]: info hashes
- Scene: [ID]: PC hashes
- [ID] Enum [online/offline]
- Scene: [ID]: PC: [ID] hashes
A user account can have many game roles. The list is placed under account: [ID]: avatars.
Each role also has a unique ID. This ID is in principle independent from the account ID, but for the sake of good human cells, the starting point of Avatar: Count is different from that of account: count.
The scenario where a role is located records the scenario name of a string Avatar: [ID]: Scene. Other data of the role is stored in a hashes.
All scenarios are indexed under World: scene. If you have multiple worlds in the future, you can use world: [ID]: scene. However, you do not need to consider it.
The online status of all PCs under scene is placed in scene: [ID]: PC hashes. The PC offline records its ID. Only the PC transfer scenario is removed.
The Location status information of each PC is recorded in scene: [ID]: PC: [ID]. The first ID is the ID of scene, and the second is the Avatar ID of the PC.
BTW. This draft is sufficient to meet the needs of the first phase of the project, even though it is not well thought. Of course, many of the issues that are not considered, but are intended to be as simple as possible to meet the needs of phase I. The cost for future modification is not high.
Finally, let's talk about the Windows version of redis. The Linux server in the office has not been installed yet. I am currently developing it in windows. Obtained an unofficial redis Windows version found by Google. For the convenience of graphs
Luajit FFI calls the hiredis DLL. At first, I was confused. The socket connection cannot be established, and the error code cannot be obtained.
After comparing the source code, we found that the modified version changed the C struct structure and added several domains before, And I defined the interface according to the official hiredis standard.
After modification, the error code can be correctly retrieved. Wsastartup is required for the Windows socks API. The modified version of hiredis windows was not called. It took me more than an hour to complete the change.
Let's talk about the API design of hiredis and rely on the layout of C struct. A good C library interface design won't do this. For example, Lua and zmq. Alas, it's a little uncomfortable to use such a thing. But it is much better than the C ++ library.