Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
pres02 committed Sep 2, 2023
2 parents 3a49636 + bfde0fa commit 6b336b0
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 35 deletions.
64 changes: 64 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: Build

on:
push:
branches: [master]
paths-ignore:
- "**.md"

pull_request:
types: [opened, reopened, synchronize]
release:
types: [published]

jobs:
build:
name: "Build"
runs-on: ubuntu-20.04
outputs:
sha: ${{ steps.declare_sha.outputs.sha }}
semver: ${{ steps.declare_sha.outputs.semver }}
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0

- name: Declare SHA & package name
id: declare_sha
shell: bash
run: |
SHA=$(git rev-parse --short HEAD)
echo "COMMIT_SHA=${SHA}" >> $GITHUB_ENV
echo "sha=$SHA" >> $GITHUB_OUTPUT
echo "semver=${{ steps.semver_parser.outputs.fullversion }}" >> $GITHUB_OUTPUT
- name: Setup AMXXPawn Compiler
uses: wopox1337/setup-amxxpawn@v1.0.2
with:
version: "1.10"

- name: Compile AMXX plugins
working-directory: scripting/
run: |
mkdir ../plugins/
for sourcefile in *.sma;
do
amxxfile="`echo $sourcefile | sed -e 's/\.sma$/.amxx/'`"
echo -n "Compiling $sourcefile ... "
amxxpc $sourcefile -o"../plugins/$amxxfile" \
-i"include" \
done
- name: Move files
run: |
mkdir publish
mv addons/ publish/
- name: Upload artifact
uses: actions/upload-artifact@v3
with:
name: player-prefs-${{ env.COMMIT_SHA }}-dev
path: publish/*
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
.compiled
.compiled
*.amxx
3 changes: 1 addition & 2 deletions import/init_.sql → migrations/create_tables.sql
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,10 @@ CREATE TABLE IF NOT EXISTS `pp_keys` (
) CHARACTER SET utf8 COLLATE utf8_general_ci;

CREATE TABLE IF NOT EXISTS `pp_preferences` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`player_id` int(11) NOT NULL,
`key_id` int(11) NOT NULL,
`value` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
PRIMARY KEY (`player_id`, `key_id`),

FOREIGN KEY (`player_id`)
REFERENCES `pp_players`(`id`)
Expand Down
14 changes: 9 additions & 5 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
# Player preferences

<p align="center" width="100%">
<img src="https://dev-cs.ru/data/resource_icons/1/1546.jpg?1679073113">
</p>

Allows you to easily manage and store player preferences, such as hats, music and other settings.

With this plugin, players can easily save and load their preferences, even on different servers. This means they can quickly and easily return to their preferred settings without having to manually adjust settings every time they join a new server.

## Usage

1. From the import folder, take the init_.sql file and import it into your database
1. From the `migrations` folder, take the `create_tables.sql` file and import it into your database
2. Put the contents of the scripting folder in the directory of your server (your_server_folder/cstrike/addons/amxmodx/scripting)
3. Compile `player_preferences.sma` [how to compile?](https://dev-cs.ru/threads/246/)
4. Add `player_preferences.amxx` into your `plugins.ini` file
3. Compile `player_prefs.sma` [how to compile?](https://dev-cs.ru/threads/246/)
4. Add `player_prefs.amxx` into your `plugins.ini` file
5. Restart server or change map
6. After restarting the server or changing the map, a config will be created in the folder `/cstrike/addons/amxmodx/configs/plugins` with the name `plugin-player_preferences.cfg`. In this config, enter the data to connect to your database
6. After restarting the server or changing the map, a config will be created in the folder `/cstrike/addons/amxmodx/configs/plugins` with the name `plugin-player_prefs.cfg`. In this config, enter the data to connect to your database
7. Use [API](https://github.com/ufame/player-preferences/blob/master/scripting/include/player_prefs.inc) to create your own plugins that allow you to save user preferences!

## Example
Expand Down Expand Up @@ -48,4 +52,4 @@ public music_command(id) {
```

Another see [pp_test.sma](https://github.com/ufame/player-preferences/blob/master/scripting/pp_test.sma). This plugin is only for testing, but it fully works
Another see [pp_test.sma](https://github.com/ufame/player-preferences/blob/master/scripting/pp_test.sma). This plugin is only for testing, but it fully works
20 changes: 14 additions & 6 deletions scripting/include/player_prefs.inc
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ forward pp_player_saved(const id);
*
* @return True if the player's preferences have been loaded, false otherwise.
*/
native pp_is_loaded(const id);
native bool: pp_is_loaded(const id);

/**
* Gets the value of a player's preference and copies it into the specified buffer.
Expand All @@ -44,7 +44,7 @@ native pp_is_loaded(const id);
*
* @return True if the preference was found and copied, false otherwise.
*/
native pp_get_preference(const id, const key[], dest[], len);
native bool: pp_get_preference(const id, const key[], dest[], len);

/**
* Sets the value of a player's preference.
Expand Down Expand Up @@ -74,11 +74,15 @@ native pp_set_key_default_value(const key[], const default_value[]);
* @param id Player ID to get preference for
* @param key Key of the preference to get
*
* @return The preference value as a number, or 0 if the preference was not found or
* @return The preference value as a number, or -1 if the preference was not found
*/
stock pp_get_num(const id, const key[]) {
new temp[64];
pp_get_preference(id, key, temp, charsmax(temp));
new bool: result = pp_get_preference(id, key, temp, charsmax(temp));

if (!result) {
return -1;
}

return str_to_num(temp);
}
Expand All @@ -89,11 +93,15 @@ stock pp_get_num(const id, const key[]) {
* @param id Player ID to get preference for
* @param key Key of the preference to get
*
* @return The preference value as a floating-point number, or 0.0 if the preference was
* @return The preference value as a floating-point number, or -1.0 if the preference was not found
*/
stock Float: pp_get_float(const id, const key[]) {
new temp[64];
pp_get_preference(id, key, temp, charsmax(temp));
new bool: result = pp_get_preference(id, key, temp, charsmax(temp));

if (!result) {
return -1.0;
}

return str_to_float(temp);
}
Expand Down
55 changes: 35 additions & 20 deletions scripting/player_prefs.sma
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ new g_iPlayerDatabaseId[MAX_PLAYERS + 1];
new Trie: g_tPlayerPreferences[MAX_PLAYERS + 1];

new Trie: g_tKeys;
new Trie: g_tKeysIds;

new g_szSqlHost[32], g_szSqlUser[32], g_szSqlPassword[128], g_szSqlDatabase[32];
new g_iForwards[Forwards];
Expand All @@ -36,7 +37,7 @@ new Handle: g_hSqlTuple;
new bool: g_bDebugMode;

public plugin_init() {
register_plugin("Player preferences", "1.0.0", "ufame");
register_plugin("Player preferences", "1.1.0", "ufame");

CreateForwards();
CreateCvars();
Expand Down Expand Up @@ -137,8 +138,8 @@ public bool: native_set_key_default_value(iPlugin, iArgs) {
if (g_hSqlTuple != Empty_Handle) {
formatex(g_szQuery, charsmax(g_szQuery),
"INSERT INTO `pp_keys` (`key`, `default_value`) VALUES ('%s', '%s') \
ON DUPLICATE KEY UPDATE `default_value` = '%s';",
szKey, szDefaultValue, szDefaultValue
ON DUPLICATE KEY UPDATE `default_value` = VALUES(`default_value`);",
szKey, szDefaultValue
);

__debug("Insert key %s <%s>: %s", szKey, szDefaultValue, g_szQuery);
Expand All @@ -149,12 +150,18 @@ public bool: native_set_key_default_value(iPlugin, iArgs) {
SQL_ThreadQuery(g_hSqlTuple, "ThreadQuery_Handler", g_szQuery, iData, sizeof iData);
}

if (g_tKeys == Invalid_Trie)
g_tKeys = TrieCreate();

return bool: TrieSetString(g_tKeys, szKey, szDefaultValue);
}

stock LoadPreferences(iPlayer) {
if (g_hSqlTuple == Empty_Handle)
if (g_hSqlTuple == Empty_Handle) {
ExecuteForward(g_iForwards[Forward_PlayerLoaded], _, iPlayer);

return;
}

new szAuth[MAX_AUTHID_LENGTH];
get_user_authid(iPlayer, szAuth, charsmax(szAuth));
Expand All @@ -174,16 +181,20 @@ stock LoadPreferences(iPlayer) {

stock SetPreference(iPlayer, szKey[], szValue[], szDefaultValue[]) {
if (g_hSqlTuple != Empty_Handle) {
// TODO: Было бы неплохо, при наличии ключа в g_tKeysIds, слать сразу значения, и только в ином случае начинать с ключа
// И даже если начинать с ключа, в колбеке всё ровно перепроверять g_tKeysIds (szData[3]) т.к. сраная асинхронщина
formatex(g_szQuery, charsmax(g_szQuery),
"INSERT INTO `pp_keys` (`key`, `default_value`) VALUES ('%s', '%s') \
ON DUPLICATE KEY UPDATE `default_value` = '%s';",
szKey, szDefaultValue, szDefaultValue
ON DUPLICATE KEY UPDATE `default_value` = VALUES(`default_value`);",
szKey, szDefaultValue
);

// TODO: Было бы неплохо в глобаг скоп выкинуть
enum data {
query_state,
player_id,
player_userid,
key_id,
value[256]
};

Expand All @@ -192,6 +203,7 @@ stock SetPreference(iPlayer, szKey[], szValue[], szDefaultValue[]) {
szData[query_state] = State_InsertKey;
szData[player_id] = iPlayer;
szData[player_userid] = get_user_userid(iPlayer);
TrieGetCell(g_tKeysIds, szKey, szData[key_id]);
formatex(szData[value], charsmax(szData[value]), szValue);

__debug("Insert key %s: %d / %d - %s",
Expand All @@ -216,13 +228,18 @@ public ThreadQuery_Handler(iFailState, Handle: hQuery, szError[], iError, szData

switch (szData[0]) {
case State_LoadKeys: {
new szKey[64], szDefaultValue[256];
new szKey[64], szDefaultValue[256], sKeyId[11];

TrieDestroy(g_tKeysIds);
g_tKeysIds = TrieCreate();

while (SQL_MoreResults(hQuery)) {
SQL_ReadResult(hQuery, SQL_FieldNameToNum(hQuery, "id"), sKeyId, charsmax(sKeyId));
SQL_ReadResult(hQuery, SQL_FieldNameToNum(hQuery, "key"), szKey, charsmax(szKey));
SQL_ReadResult(hQuery, SQL_FieldNameToNum(hQuery, "default_value"), szDefaultValue, charsmax(szDefaultValue));

TrieSetString(g_tKeys, szKey, szDefaultValue);
TrieSetCell(g_tKeysIds, szKey, str_to_num(sKeyId));

SQL_NextRow(hQuery);
}
Expand Down Expand Up @@ -330,16 +347,15 @@ public ThreadQuery_Handler(iFailState, Handle: hQuery, szError[], iError, szData
if (iUserid != get_user_userid(iPlayer))
return;

new szValue[256];
formatex(szValue, charsmax(szValue), szData[3], charsmax(szData[]));
new iKeyId = szData[3] == 0 ? SQL_GetInsertId(hQuery) : szData[3];

formatex(g_szQuery, charsmax(g_szQuery),
"INSERT INTO `pp_preferences` (`player_id`, `key_id`, `value`) VALUES (%d, %d, '%s') \
ON DUPLICATE KEY UPDATE `value` = '%s';",
g_iPlayerDatabaseId[iPlayer], SQL_GetInsertId(hQuery), szValue, szValue
ON DUPLICATE KEY UPDATE `value` = VALUES(`value`);",
g_iPlayerDatabaseId[iPlayer], iKeyId, szData[4]
);

__debug("State: Insert key #3 <%d><%d><%d><%s>: %s", iPlayer, iUserid, get_user_userid(iPlayer), szValue, g_szQuery);
__debug("State: Insert key #3 <%d><%d><%d><%s>: %s", iPlayer, iUserid, get_user_userid(iPlayer), szData[4], g_szQuery);

szData[0] = State_InsertPreference;

Expand Down Expand Up @@ -376,42 +392,41 @@ CreateCvars() {
}

CreateForwards() {
g_iForwards[Forward_Initialized] = CreateMultiForward("pp_init", ET_IGNORE);
g_iForwards[Forward_Initialized] = CreateMultiForward("pp_init", ET_IGNORE, FP_CELL);
g_iForwards[Forward_PlayerLoaded] = CreateMultiForward("pp_player_loaded", ET_IGNORE, FP_CELL);
g_iForwards[Forward_PlayerSaved] = CreateMultiForward("pp_player_saved", ET_IGNORE, FP_CELL);
}

ConnectionTest() {
SQL_SetAffinity("mysql");
g_hSqlTuple = SQL_MakeDbTuple(g_szSqlHost, g_szSqlUser, g_szSqlPassword, g_szSqlDatabase);

new szError[512], iErrorCode, retVal;
new szError[512], iErrorCode;
new Handle: hConnection = SQL_Connect(g_hSqlTuple, iErrorCode, szError, charsmax(szError));

if (hConnection == Empty_Handle) {
SQL_FreeHandle(g_hSqlTuple);
g_hSqlTuple = Empty_Handle;

log_error(AMX_ERR_NATIVE, "[PP] Connection error[%d]: %s", iErrorCode, szError);
abort(AMX_ERR_NATIVE, "[PP] Connection error[%d]: %s", iErrorCode, szError);

ExecuteForward(g_iForwards[Forward_Initialized], retVal);
ExecuteForward(g_iForwards[Forward_Initialized], _, false);

return;
}

log_amx("[PP] Connection to database successfully estabilished");

g_tKeys = TrieCreate();

formatex(g_szQuery, charsmax(g_szQuery),
"SELECT * FROM `pp_keys`"
"SELECT `id`, `key`, `default_value` FROM `pp_keys`"
);

new iData[1];
iData[0] = State_LoadKeys;

SQL_ThreadQuery(g_hSqlTuple, "ThreadQuery_Handler", g_szQuery, iData, sizeof iData);

ExecuteForward(g_iForwards[Forward_Initialized], retVal);
ExecuteForward(g_iForwards[Forward_Initialized], _, true);
}

SQL_ThreadError(Handle: hQuery, szError[], iError, Float: flQueueTime) {
Expand Down
2 changes: 1 addition & 1 deletion scripting/pp_test.sma
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public pp_handler(id) {

menu_addtext(menuId, fmt("Current X \y%.2f Y \y%.2f",
g_playerData[id][HudPosition_X], g_playerData[id][HudPosition_Y]
));
), 0);
menu_additem(menuId, "Hud X +");
menu_additem(menuId, "Hud Y +");
menu_additem(menuId, "Hud X -");
Expand Down

0 comments on commit 6b336b0

Please sign in to comment.