diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..736b00f --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,64 @@ +name: Build Flow + +on: + push: + branches: + - dev + pull_request: + branches: + - dev + +jobs: + build: + name: Build + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + sm-version: [ '1.12.x' ] + + steps: + - name: Checkout + uses: actions/checkout@v7 + with: + submodules: recursive + + - name: Setup SourcePawn ${{ matrix.sm-version }} + uses: rumblefrog/setup-sp@master + with: + version: ${{ matrix.sm-version }} + + - name: Setup Build Env + run: | + mkdir -p build/ccp build/include + + cp -r -f ccp-* build/ + cp -r -f ccp/* build/ccp/ + cp -r -f include/* build/include/ + + cp -r -f third-party/packager/include/* build/include/ + cp -r -f third-party/storage/src/include/* build/include/ + cp -r -f third-party/sm-jansson/pawn/scripting/include/* build/include/ + cp -r -f third-party/shop/addons/sourcemod/scripting/include/* build/include/ + cp -r -f third-party/vip/addons/sourcemod/scripting/include/* build/include/ + + for dir in build/ccp-*/; do + mkdir -p "$dir/plugins" 2>/dev/null + done + + mkdir -p build/ccp/plugins + + - name: Build All + working-directory: build + run: | + for dir in ccp-*/; do + folder_name=$(basename "$dir") + + s_file=$folder_name/scripting/$folder_name.sp + o_file=$folder_name/plugins/$folder_name.smx + + echo "Compiling $s_file ..." + + spcomp $s_file -O2 -v2 -iinclude -o $o_file + done + \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 2890856..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: CI - -on: - push: - branches: - - dev - pull_request: - branches: - - dev - -jobs: - build: - name: Build - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - sm-version: [ '1.12.x' ] - - steps: - - name: Checkout - uses: actions/checkout@v7 - with: - path: build - - - uses: actions/checkout@v7 - - - name: Setup SourcePawn - uses: rumblefrog/setup-sp@master - with: - version: ${{ matrix.sm-version }} - - - name: Setup Working Directory - run: | - mkdir -p sourcemod/plugins - mkdir -p sourcemod/scripting - - cp -r -f src/* sourcemod/scripting/ - - - name: Build - working-directory: src - run: | - for file in *.sp; - do - filename="${file%.*}" - - echo "Compiling $filename..." - spcomp $file -O2 -v2 -iinclude -o ../sourcemod/plugins/$filename.smx - done \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1b62854..b3f4c09 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,42 +17,71 @@ jobs: - name: Checkout uses: actions/checkout@v7 with: - path: build + submodules: recursive - - uses: actions/checkout@v7 + - name: Tag + id: get_tag + run: | + TAG=${GITHUB_REF#refs/tags/} + echo "tag=$TAG" >> $GITHUB_OUTPUT + echo "TAG=$TAG" >> $GITHUB_ENV - - name: Setup SourcePawn + - name: Setup SourcePawn ${{ matrix.sm-version }} uses: rumblefrog/setup-sp@master with: version: ${{ matrix.sm-version }} - - name: Setup Working Directory + - name: Setup Build Env run: | - mkdir -p sourcemod/plugins - mkdir -p sourcemod/scripting + mkdir -p build/ccp build/include + + cp -r -f ccp-* build/ + cp -r -f ccp/* build/ccp/ + cp -r -f include/* build/include/ + + cp -r -f third-party/packager/include/* build/include/ + cp -r -f third-party/storage/src/include/* build/include/ + cp -r -f third-party/sm-jansson/pawn/scripting/include/* build/include/ + cp -r -f third-party/shop/addons/sourcemod/scripting/include/* build/include/ + cp -r -f third-party/vip/addons/sourcemod/scripting/include/* build/include/ + + for dir in build/ccp-*/; do + mkdir -p "$dir/plugins" 2>/dev/null + done + + mkdir -p build/ccp/plugins - cp -r -f src/* sourcemod/scripting/ - - name: Build - working-directory: src + working-directory: build run: | - for file in *.sp; - do - filename="${file%.*}" + for dir in ccp-*/; do + folder_name=$(basename "$dir") - echo "Compiling $filename..." - spcomp $file -O2 -v2 -iinclude -o ../sourcemod/plugins/$filename.smx + s_file=$folder_name/scripting/$folder_name.sp + o_file=$folder_name/plugins/$folder_name.smx + + echo "Compiling $s_file ..." + + spcomp $s_file -O2 -v2 -iinclude -o $o_file done - name: Package - working-directory: sourcemod - run: 7z a ccprocessor-${{ matrix.sm-version }}.zip ./* + working-directory: build + run: | + TAG="${{ steps.get_tag.outputs.tag }}" + + 7z a ccp-${TAG}.${{ github.run_number }}-bundle.zip ./* + + for dir in ccp-*/; do + name=$(basename "$dir") + + (cd $dir && 7z a "../${name}-${TAG}.${{ github.run_number }}.zip" ./*) + done - name: Attach extension uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} - file: 'sourcemod/ccprocessor-${{ matrix.sm-version }}.zip' - asset_name: ccprocessor-$tag.${{ github.run_number }}.zip + file: 'build/*.zip' tag: ${{ github.ref }} - file_glob: false \ No newline at end of file + file_glob: true \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..f051e8e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,17 @@ +[submodule "third-party/packager"] + path = third-party/packager + url = https://github.com/rej-clown/packager.git +[submodule "third-party/storage"] + path = third-party/storage + url = https://github.com/rej-clown/storage.git +[submodule "third-party/sm-jansson"] + path = third-party/sm-jansson + url = https://github.com/rejchev/sm-jansson.git + branch = main +[submodule "third-party/shop"] + path = third-party/shop + url = https://github.com/hlmod/Shop-Core.git + branch = master +[submodule "third-party/vip"] + path = third-party/vip + url = https://github.com/R1KO/VIP-Core.git diff --git a/README.md b/README.md index 939eb41..f00236c 100644 --- a/README.md +++ b/README.md @@ -57,8 +57,6 @@ Handlers intercept and expand the engine’s message channels by virtualizing th | `TextMsg` | `ccp-textmsg.smx` | `Yes` | `TM` = Server chat | | `SayText` | `ccp-saytext.smx` | `Yes` | `ST` = Server chat | | `RadioText` | `ccp-radiomsg.smx` | `Yes` | `RT` = Radio chat | - -## [Supported Modules](https://github.com/rejchev/ccp-modules) ## License [GNU Public License v3](https://github.com/rejchev/ccprocessor/blob/main/LICENSE) diff --git a/ccp-admin-channel/scripting/ccp-admin-channel.sp b/ccp-admin-channel/scripting/ccp-admin-channel.sp new file mode 100644 index 0000000..af6463f --- /dev/null +++ b/ccp-admin-channel/scripting/ccp-admin-channel.sp @@ -0,0 +1,329 @@ +#pragma newdecls required + +#include + +public Plugin myinfo = +{ + name = "[CCP] Admin channel", + author = "rej.chev", + description = "...", + version = "2.0.0", + url = "https://discord.gg/cFZ97Mzrjy" +}; + +static const char pkgKey[] = "admin_channel"; + +JsonObject jConfig; +JsonObject jMessager; + +int counter; + +bool g_bLate; + +static const char ROOT[] = "z"; + +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { + #if defined DEBUG + DBUILD() + #endif + + g_bLate = late; + return APLRes_Success; +} + +public void OnPluginStart() { + LoadTranslations("admin-channel.phrases"); +} + +public void OnMapStart() { + #if defined DEBUG + DBUILD() + #endif + + // Handshake + cc_proc_APIHandShake(cc_get_APIKey()); + + // late load + if(g_bLate) { + g_bLate = false; + + for(int i; i <= MaxClients; i++) + if(Packager.GetPackage(i)) + pckg_OnPackageAvailable(i); + } + + counter = 0; + + delete jMessager; + jMessager = asJSONO(new Json("{}")); +} + +public void pckg_OnPackageAvailable(int iClient) { + static char config[MESSAGE_LENGTH] + = "configs/ccprocessor/admins-channel/settings.json"; + + if(iClient) + return; + + // Load from local + if(config[0] == 'c') { + BuildPath(Path_SM, config, sizeof(config), config); + } + + if(!FileExists(config)) { + SetFailState("Config file is not exists: %s", config); + } + + delete jConfig; + + if(!Packager.GetPackage(iClient).SetArtifact(pkgKey, (jConfig = Json.JsonF(config, 0)))) + SetFailState("Something went wrong on set artifact '%s' for client %d", pkgKey, iClient); +} + +public void pckg_OnPackageUpdated(Handle plugin, int iClient) { + if(iClient) + return; + + char szBuffer[MAX_NAME_LENGTH]; + GetPluginFilename(plugin, szBuffer, sizeof(szBuffer)); + + if(strcmp(szBuffer, "ccp-channel-mgr.smx")) + return; + + char szMyself[MAX_NAME_LENGTH]; + jConfig.GetString("identificator", szMyself, sizeof(szMyself)); + + if(ccp_FindChannel(szMyself) == -1) + ccp_AddChannel(szMyself); + +} + +public Action OnClientSayCommand(int iClient, const char[] cmd, const char[] args) { + static int rootFlag; + if(!rootFlag) { + rootFlag = ReadFlagString(ROOT); + } + + if(!iClient || !IsClientInGame(iClient) || IsChatTrigger()) { + return Plugin_Continue; + } + + static char buffer[MAX_LENGTH]; + FormatEx(buffer, sizeof(buffer), "%s", args); + + TrimString(buffer); + + static char trigger[4]; + jConfig.GetString("channelTrigger", trigger, sizeof(trigger)); + + if(buffer[0] == trigger[0]) { + Format(buffer, sizeof(buffer), "%s", buffer[1]); + + TrimString(buffer); + + if(!buffer[0]) { + return Plugin_Continue; + } + + static int clientFlags; + clientFlags = GetUserFlagBits(iClient); + + static int accessFlag; + jConfig.GetString("accessFlag", trigger, sizeof(trigger)); + accessFlag = ReadFlagString(trigger); + + static bool playersCanComplain; + playersCanComplain = jConfig.GetBool("playersCanComplain"); + + if(accessFlag && ((clientFlags & accessFlag) || (clientFlags & rootFlag) || playersCanComplain)) { + JsonObject message = asJSONO(new Json("{}")); + + static char clientName[NAME_LENGTH]; + GetClientName(iClient, clientName, sizeof(clientName)); + + message.SetInt("senderId", GetClientUserId(iClient)); + message.SetString("senderName", clientName); + message.SetBool("isAdmin", ((clientFlags & accessFlag) || (clientFlags & rootFlag))); + message.SetString("body", buffer); + + static char msgPointer[MESSAGE_LENGTH]; + FormatEx(msgPointer, sizeof(msgPointer), "msgObject@%d", counter++); + + jMessager.Set(msgPointer, message); + delete message; + + for(int i = 1, a; i <= MaxClients; i++) { + if(IsClientInGame(i)) { + a = GetUserFlagBits(i); + if((a & accessFlag) || (a & rootFlag)) { + PrintToChat(i, msgPointer); + } + } + } + + // don't post call + return Plugin_Stop; + } + } + + return Plugin_Continue; +} + +char nextMessage[MESSAGE_LENGTH]; + +public Processing cc_proc_OnNewMessage(const int[] props, int propsCount, ArrayList params) { + static const char parentChannel[] = "TM"; + + char szBuffer[MESSAGE_LENGTH]; + params.GetString(0, szBuffer, sizeof(szBuffer)); + + nextMessage = NULL_STRING; + + if(strcmp(szBuffer, parentChannel) != 0 || IsClientSourceTV(props[1])) { + return Proc_Continue; + } + + // message body + params.GetString(2, szBuffer, sizeof(szBuffer)); + if(!jMessager.HasKey(szBuffer)) { + return Proc_Continue; + } + + FormatEx(nextMessage, sizeof(nextMessage), "%s", szBuffer); + + JsonObject msg = asJSONO(jMessager.Get(szBuffer)); + int sender = GetClientOfUserId(msg.GetInt("senderId")); + + jConfig.GetString("identificator", szBuffer, sizeof(szBuffer)); + params.SetString(0, szBuffer); + + msg.GetString("body", szBuffer, sizeof(szBuffer)); + + if(jConfig.GetBool("useLog")) { + LogAction(sender, -1, "\"%L\" (%s) used admin channel (text %s)", sender, msg.GetBool("isAdmin") ? "Admin" : "Player", szBuffer); + } + + delete msg; + return Proc_Change; +} + +public Processing cc_proc_OnRebuildString(const int[] props, int part, ArrayList params, int &level, char[] value, int size) { + static int rootFlag; + if(!rootFlag) { + rootFlag = ReadFlagString(ROOT); + } + + char szIndent[64]; + params.GetString(0, szIndent, sizeof(szIndent)); + + char szBuffer[MESSAGE_LENGTH]; + jConfig.GetString("identificator", szBuffer, sizeof(szBuffer)); + + if(strcmp(szIndent, szBuffer) || !nextMessage[0] || !jMessager.HasKey(nextMessage)) { + return Proc_Continue; + } + + JsonObject message = asJSONO(jMessager.Get(nextMessage)); + + static int senderId; + senderId = GetClientOfUserId(message.GetInt("senderId")); + + static bool isSenderAdmin; + isSenderAdmin = message.GetBool("isAdmin"); + + static char senderName[NAME_LENGTH]; + message.GetString("senderName", senderName, sizeof(senderName)); + + static char msgBody[MESSAGE_LENGTH]; + message.GetString("body", msgBody, sizeof(msgBody)); + + delete message; + + static bool isSenderAlive; + isSenderAlive = senderId && IsClientInGame(senderId) && IsPlayerAlive(senderId); + + if(jConfig.HasKey("priority")) { + int prio = jConfig.GetInt("priority"); + + if(prio < level) { + return Proc_Continue; + } + + level = prio; + } + + JsonArray jValues; + + // level = 99; + + switch(part) { + case BIND_PROTOTYPE: { + if(jConfig.HasKey(szBinds[part]) && jConfig.GetString(szBinds[part], value, size)) { + Format(value, size, "%c %T", 1, value, senderId); + } + } + + case BIND_STATUS, BIND_STATUS_CO: { + if(jConfig.HasKey(szBinds[part]) && (jValues = asJSONA(jConfig.Get(szBinds[part]))) && jValues.Size) { + jValues.GetString(view_as(isSenderAlive), value, size); + Format(value, size, "%T", value, props[2]); + } + } + + case BIND_TEAM, BIND_TEAM_CO: { + + if(jConfig.HasKey(szBinds[part]) && (jValues = asJSONA(jConfig.Get(szBinds[part]))) && jValues.Size) { + jValues.GetString( + // view: admin to admin + (isSenderAdmin) + ? 0 : + // view: sender - user and how he sees this msg + (!isSenderAdmin && senderId == props[2]) + ? 2 + // view: other options + : 1, + value, size + ); + + Format(value, size, "%T", value, props[2]); + } + } + + case BIND_NAME: { + FormatEx(value, size, "%s", senderName); + } + + case BIND_MSG: { + FormatEx(value, size, "%s", msgBody); + } + + default: { + if(jConfig.HasKey(szBinds[part]) && jConfig.GetString(szBinds[part], value, size)) { + FormatEx(value, size, "%T", value, props[2]); + } + + Package pack; + JsonObject jArtifact; + JsonObject jSync; + + if((pack = Packager.GetPackage(0)) && pack.HasArtifact("synchronizer")) { + jArtifact = asJSONO(pack.GetArtifact("synchronizer")); + + if(jArtifact.HasKey(szIndent)) + jSync = asJSONO(jArtifact.Get(szIndent)); + + delete jArtifact; + + // Delegating values from the main message thread + if(jSync && jSync.HasKey(szBinds[part])) + if(part == BIND_PREFIX || jConfig.GetBool("delegate")) + jSync.GetString(szBinds[part], value, size); + + delete jSync; + } + } + } + + delete jValues; + return Proc_Change; +} \ No newline at end of file diff --git a/ccp-channel-filter/scripting/ccp-channel-filter.sp b/ccp-channel-filter/scripting/ccp-channel-filter.sp new file mode 100644 index 0000000..efe6c20 --- /dev/null +++ b/ccp-channel-filter/scripting/ccp-channel-filter.sp @@ -0,0 +1,180 @@ +#pragma newdecls required + +#include + +public Plugin myinfo = +{ + name = "[CCP] Channels Filter", + author = "rej.chev", + description = "...", + version = "2.0.0", + url = "https://t.me/nyoood" +}; + +static const char pkgKey[] = "channels_filter"; + +bool g_bLate; + +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { + g_bLate = late; + return APLRes_Success; +} + +public void OnPluginStart() { + LoadTranslations("ccp_channels.phrases"); + RegConsoleCmd("sm_channels", cmd); +} + +public void OnMapStart() { + // Handshake + cc_proc_APIHandShake(cc_get_APIKey()); + + // late load + if(g_bLate) { + g_bLate = false; + + for(int i; i <= MaxClients; i++) + if(Packager.GetPackage(i)) + pckg_OnPackageAvailable(i); + } +} + +public void pckg_OnPackageAvailable(int iClient) { + if(!iClient) + return; + + if(!Packager.GetPackage(iClient).SetArtifact(pkgKey, new Json("[]"), freeAnyway)) + SetFailState("Something went wrong on set artifact '%s' for client %d", pkgKey, iClient); +} + +Action cmd(int iClient, int args) { + if(!iClient || !IsClientInGame(iClient)) + return Plugin_Handled; + + Package pack; + if(!(pack = Packager.GetPackage(iClient))) + return Plugin_Handled; + + JsonArray objChannels; + JsonArray objClientFilter; + + if(!(objChannels = asJSONA(ccp_GetChannelList())) + || !objChannels.Size + || !(objClientFilter = asJSONA(pack.GetArtifact(pkgKey)))) { + delete objChannels; + delete objClientFilter; + + return Plugin_Handled; + } + + Menu hMenu; + hMenu = new Menu(MenuCallBack); + hMenu.SetTitle("%T \n \n", "title", iClient); + + char szBuffer[MESSAGE_LENGTH], szValue[MESSAGE_LENGTH]; + for(int i; i < objChannels.Size; i++) { + objChannels.GetString(i, szValue, sizeof(szValue)); + FormatEx( + szBuffer, sizeof(szBuffer), "%T", + (!objClientFilter || GetIndexOfIndent(objClientFilter, szValue) == -1) + ? "enabled" + : "disabled", + iClient + ); + + Format(szValue, sizeof(szValue), "%T", szValue, iClient); + Format(szBuffer, sizeof(szBuffer), "%c%T", i+1, "item_channel", iClient, szValue, szBuffer); + + hMenu.AddItem(szBuffer, szBuffer[1]); + } + + delete objChannels; + delete objClientFilter; + + hMenu.Display(iClient, MENU_TIME_FOREVER); + + return Plugin_Handled; +} + +public int MenuCallBack(Menu hMenu, MenuAction action, int iClient, int option) +{ + switch(action) + { + case MenuAction_End: delete hMenu; + case MenuAction_Select: { + char szBuffer[MESSAGE_LENGTH]; + hMenu.GetItem(option, szBuffer, sizeof(szBuffer)); + + int item = szBuffer[0] - 1; + + Package pack = Packager.GetPackage(iClient); + + JsonArray objChannels = asJSONA(ccp_GetChannelList()); + JsonArray objClientFilter = asJSONA(pack.GetArtifact(pkgKey)); + + char szValue[MESSAGE_LENGTH]; + objChannels.GetString(item, szValue, sizeof(szValue)); + delete objChannels; + + item = GetIndexOfIndent(objClientFilter, szValue); + + if(item == -1) { + objClientFilter.PushString(szValue); + + } else { + objClientFilter.Remove(item); + } + + pack.SetArtifact(pkgKey, objClientFilter, freeAnyway); + + FormatEx(szBuffer, sizeof(szBuffer), "%T", (item != -1) ? "enabled" : "disabled", iClient); + Format(szValue, sizeof(szValue), "%T", szValue, iClient); + + PrintToChat(iClient, "%T", "chat_setvalue", iClient, szValue, szBuffer); + cmd(iClient, 0); + } + } +} + +public Processing cc_proc_OnRebuildString(const int[] props, int part, ArrayList params, int &level, char[] value, int size) { + + if(part != BIND_PROTOTYPE) + return Proc_Continue; + + static char szIndent[64]; + params.GetString(0, szIndent, sizeof(szIndent)); + + Package pack; + JsonArray channelList; + for(int i, a; i < 2; i++) { + a = ((!i) ? SENDER_INDEX(props[i+1]) : props[i+1]); + + if(!a || !(pack = Packager.GetPackage(a))) + continue; + + if(!(channelList = asJSONA(pack.GetArtifact(pkgKey)))) + continue; + + if(channelList.Size && GetIndexOfIndent(channelList, szIndent) != -1) { + delete channelList; + return Proc_Stop; + } + + delete channelList; + } + + return Proc_Continue; +} + +stock int GetIndexOfIndent(JsonArray obj, const char[] indent) { + static char szBuffer[64]; + for(int i; i < obj.Size; i++) { + obj.GetString(i, szBuffer, sizeof(szBuffer)); + + if(!strcmp(szBuffer, indent)) { + return i; + } + } + + return -1; +} \ No newline at end of file diff --git a/ccp-channel-manager/scripting/ccp-channel-manager.sp b/ccp-channel-manager/scripting/ccp-channel-manager.sp new file mode 100644 index 0000000..b733e51 --- /dev/null +++ b/ccp-channel-manager/scripting/ccp-channel-manager.sp @@ -0,0 +1,72 @@ +#pragma newdecls required + +#include +#include + +public Plugin myinfo = +{ + name = "[CCP] Channel manager", + author = "rej.chev?", + description = "...", + version = "2.0.0", + url = "https://discord.gg/cFZ97Mzrjy" +}; + +static const char pkgKey[] = "channel_mgr"; + +bool g_bLate; + +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { + #if defined DEBUG + DBUILD() + #endif + + g_bLate = late; + return APLRes_Success; +} + +public void OnMapStart() { + #if defined DEBUG + DBUILD() + #endif + + // Handshake + cc_proc_APIHandShake(cc_get_APIKey()); + + // late load + if(g_bLate) { + g_bLate = false; + + if(Packager.GetPackage(0)) + pckg_OnPackageAvailable(0); + } + +} + +public void pckg_OnPackageAvailable(int iClient) { + if(iClient) + return; + + static const char handlers[4][] = { + "ccp-saytext2.smx", "ccp-radiomsg.smx", "ccp-textmsg.smx", "ccp-saytext.smx" + }; + + static const char nativeChannels[4][PREFIX_LENGTH] = { + "", "RT", "TM", "ST" + }; + + JsonArray channels = asJSONA(new Json("[]")); + for(int i; i < sizeof(handlers); i++) { + if(FindPluginByFile(handlers[i])) { + if(!i) { + channels.PushString("STA"); + channels.PushString("STP"); + channels.PushString("CN"); + } + + else channels.PushString(nativeChannels[i]); + } + } + + Packager.GetPackage(iClient).SetArtifact(pkgKey, channels, freeAnyway); +} \ No newline at end of file diff --git a/ccp-chatwheel/scripting/ccp-chatwheel.sp b/ccp-chatwheel/scripting/ccp-chatwheel.sp new file mode 100644 index 0000000..5ae2d8b --- /dev/null +++ b/ccp-chatwheel/scripting/ccp-chatwheel.sp @@ -0,0 +1,26 @@ +#pragma newdecls required + +#include + +public Plugin myinfo = +{ + name = "[CCP] ChatWheel Fix", + author = "rejchev", + description = "...", + version = "1.0.2", + url = "https://t.me/nyoood" +}; + +public Processing cc_proc_OnRebuildString(const int[] props, int part, ArrayList params, int &level, char[] value, int size) { + char indent[64]; + params.GetString(0, indent, sizeof(indent)); + + if(indent[0] == 'R' && indent[1] == 'T' && strlen(indent) == 2) { + if(part == BIND_NAME && !value[0]) { + GetClientName(props[2], value, size); + return Proc_Change; + } + } + + return Proc_Continue; +} \ No newline at end of file diff --git a/ccp-clantag/scripting/ccp-clantag.sp b/ccp-clantag/scripting/ccp-clantag.sp new file mode 100644 index 0000000..f581c06 --- /dev/null +++ b/ccp-clantag/scripting/ccp-clantag.sp @@ -0,0 +1,67 @@ +#pragma newdecls required + +#include + +#include + +public Plugin myinfo = +{ + name = "[CCP] Clan Tag as Chat Tag", + author = "rejchev?", + description = "...", + version = "1.3.3", + url = "discord.gg/ChTyPUG" +}; + +public void OnPluginStart() +{ + CreateConVar("ccp_clantag_priority", "1", "Priority for replacing the prefix value", _, true, 0.0).AddChangeHook(OnConVarChanged); + + AutoExecConfig(true, "clantag", "ccprocessor"); +} + +public void OnMapStart() +{ + cc_proc_APIHandShake(cc_get_APIKey()); + + OnConVarChanged(FindConVar("ccp_clantag_priority"), NULL_STRING, NULL_STRING); +} + +int plevel; + +public void OnConVarChanged(ConVar cvar, const char[] oldVal, const char[] newVal) +{ + plevel = cvar.IntValue; +} + +public Processing cc_proc_OnRebuildString(const int[] props, int part, ArrayList params, int &level, char[] value, int size) { + char szIndent[64]; + params.GetString(0, szIndent, sizeof(szIndent)); + + if((szIndent[0] != 'S' && szIndent[1] != 'T' && strlen(szIndent) < 3) || !SENDER_INDEX(props[1])) { + return Proc_Continue; + } + + if(part != BIND_PREFIX || level > plevel) + return Proc_Continue; + + char szPrefix[PREFIX_LENGTH]; + szPrefix = GetClientClanTag(SENDER_INDEX(props[1])); + + if(!szPrefix[0]) + return Proc_Continue; + + level = plevel; + FormatEx(value, size, szPrefix); + + return Proc_Change; +} + +char[] GetClientClanTag(int iClient) +{ + char szTag[PREFIX_LENGTH]; + + CS_GetClientClanTag(iClient, szTag, sizeof(szTag)); + + return szTag; +} \ No newline at end of file diff --git a/ccp-ecleaner/scripting/ccp-ecleaner.sp b/ccp-ecleaner/scripting/ccp-ecleaner.sp new file mode 100644 index 0000000..586d07c --- /dev/null +++ b/ccp-ecleaner/scripting/ccp-ecleaner.sp @@ -0,0 +1,61 @@ +#pragma newdecls required + +#if defined INCLUDE_DEBUG + #define DEBUG "[EngineMsg-Cleaner]" +#endif + +#include + +public Plugin myinfo = +{ + name = "[CCP] EngineMsg Cleaner", + author = "nu11ent", + description = "...", + version = "1.0.1", + url = "https://t.me/nyoood" +}; + +ArrayList g_aEngineMsgList; + +public void OnPluginStart() { + g_aEngineMsgList = new ArrayList(MESSAGE_LENGTH, 0); +} + +public void OnMapStart() { + static char config[MESSAGE_LENGTH] = "configs/ccprocessor/enginemsg-cleaner/stash.txt"; + + if(config[0] == 'c') { + BuildPath(Path_SM, config, sizeof(config), config); + } + + if(!FileExists(config)) { + SetFailState("Stash file is not exists: %s", config); + } + + g_aEngineMsgList.Clear(); + + File hFile = OpenFile(config, "r"); + if(!hFile) { + return; + } + + char szBuffer[MESSAGE_LENGTH]; + while(!hFile.EndOfFile() && hFile.ReadLine(szBuffer, sizeof(szBuffer))) { + TrimString(szBuffer); + + if(!szBuffer[0] || (szBuffer[0] == '/' && szBuffer[1] == '/')) { + continue; + } + + g_aEngineMsgList.PushString(szBuffer); + } + + delete hFile; +} + +public Processing cc_proc_HandleEngineMsg(const int[] props, int propsCount, ArrayList params) { + char szBuffer[MESSAGE_LENGTH]; + params.GetString(0, szBuffer, sizeof(szBuffer)); + + return g_aEngineMsgList.FindString(szBuffer) == -1 ? Proc_Continue : Proc_Stop; +} \ No newline at end of file diff --git a/ccp-elogger/scripting/ccp-elogger.sp b/ccp-elogger/scripting/ccp-elogger.sp new file mode 100644 index 0000000..63e8109 --- /dev/null +++ b/ccp-elogger/scripting/ccp-elogger.sp @@ -0,0 +1,23 @@ +/* + Leaves an entry in the log about the engine message key, + in the case when it does not exist or does not have a translation for the server language in the library of phrases +*/ + +#pragma newdecls required + +#include + +public void OnMapStart() +{ + cc_proc_APIHandShake(cc_get_APIKey()); +} + +public Processing cc_proc_HandleEngineMsg(const int[] props, int propsCount, ArrayList params) +{ + char szMessage[MESSAGE_LENGTH]; + params.GetString(0, szMessage, sizeof(szMessage)); + + LogMessage("Engine message key: %s | Is phrase exists: %b ", szMessage, ccp_Translate(szMessage, props[1])); + + return Proc_Continue; +} \ No newline at end of file diff --git a/ccp-joinserver/scripting/ccp-joinserver.sp b/ccp-joinserver/scripting/ccp-joinserver.sp new file mode 100644 index 0000000..8852c1d --- /dev/null +++ b/ccp-joinserver/scripting/ccp-joinserver.sp @@ -0,0 +1,322 @@ +#pragma newdecls required + +#include + +public Plugin myinfo = +{ + name = "[CCP] Join Server", + author = "rej.chev?", + description = "Processing player connect&disconnect events", + version = "2.0.0", + url = "https://discord.gg/cFZ97Mzrjy" +}; + +static const char pkgKey[] = "join_server"; + +static const char objectPattern[] = "js@%x"; + +JsonObject jConfig; +JsonObject jMessanger; + +bool g_bLate; + +char currentObject[64]; + +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { + #if defined DEBUG + DBUILD() + #endif + + g_bLate = late; + return APLRes_Success; +} + +public void OnPluginStart() { + // Temporary solution + LoadTranslations("ccp-joinserver.phrases"); + + HookEvent("player_connect", Events, EventHookMode_Pre); + HookEvent("player_disconnect", Events, EventHookMode_Pre); +} + +public void OnMapStart() { + #if defined DEBUG + DBUILD() + #endif + + // Handshake + cc_proc_APIHandShake(cc_get_APIKey()); + + // late load + if(g_bLate) { + g_bLate = false; + + for(int i; i <= MaxClients; i++) + if(Packager.GetPackage(i)) + pckg_OnPackageAvailable(i); + } + + delete jMessanger; + jMessanger = asJSONO(new Json("{}")); +} + +public void pckg_OnPackageAvailable(int iClient) { + static char config[MESSAGE_LENGTH] = "configs/ccprocessor/join-server/settings.json"; + + if(iClient) + return; + + + if(jConfig) { + delete jConfig; + } + + // Load from local + if(config[0] == 'c') { + BuildPath(Path_SM, config, sizeof(config), config); + } + + if(!FileExists(config)) { + SetFailState("Config file is not exists: %s", config); + } + + jConfig = asJSONO(Json.JsonF(config, 0)); + + Packager.GetPackage(iClient).SetArtifact(pkgKey, jConfig); +} + +public void pckg_OnPackageUpdated(Handle plugin, int iClient) { + if(iClient) + return; + + char szBuffer[MAX_NAME_LENGTH]; + GetPluginFilename(plugin, szBuffer, sizeof(szBuffer)); + + if(strcmp(szBuffer, "ccp-channel-mgr.smx")) + return; + + char szMyself[MAX_NAME_LENGTH]; + jConfig.GetString("identificator", szMyself, sizeof(szMyself)); + + if(ccp_FindChannel(szMyself) == -1) + ccp_AddChannel(szMyself); +} + +Action Events(Event event, const char[] name, bool dbc) { + if(!jConfig.GetBool("processing")) + return Plugin_Continue; + + event.SetBool("silent", true); + event.BroadcastDisabled = true; + + if(jConfig.GetBool("hideMessages")) + return Plugin_Handled; + + // params + char szName[NAME_LENGTH]; + event.GetString("name", szName, sizeof(szName)); + + int userId = event.GetInt("userid"); + + char network[NAME_LENGTH]; + event.GetString("networkid", network, sizeof(network)); + + int bot = event.GetInt("bot"); + + int index = -1; + char buffer[NAME_LENGTH]; + if(name[7] == 'd') + event.GetString("reason", buffer, sizeof(buffer)); + + else { + event.GetString("address", buffer, sizeof(buffer)); + index = event.GetInt("index"); + } + + JsonObject message = asJSONO(new Json("{}")); + message.SetString("event", name); + message.SetInt("userid", userId); + message.SetInt("index", index); + message.SetInt("bot", bot); + message.SetString("name", szName); + message.SetString("network", network); + message.SetString(name[7] == 'd' ? "reason" : "address", buffer); + + // event address + FormatEx(szName, sizeof(szName), objectPattern, event); + + jMessanger.Set(szName, message); + delete message; + + // for(int i = 1; i <= MaxClients; i++) + // if(IsClientInGame(i)) + // PrintToChat(i, szName); + + DataPack dp = new DataPack(); + dp.Reset(); + dp.WriteString(szName); + + RequestFrame(OnNextFrame, dp); + + return Plugin_Handled; +} + +public void OnNextFrame(DataPack data) +{ + if(!data) + return; + + data.Reset(); + + char szIdent[NAME_LENGTH]; + data.ReadString(szIdent, sizeof(szIdent)); + + delete data; + + Handle uMessage; + UserMessageType uType = GetUserMessageType(); + for(int i = 1, j; i <= MaxClients; i++) { + j = 1; + if(IsClientConnected(i)) { + if((uMessage = StartMessageOne("TextMsg", i, USERMSG_RELIABLE)) != null) { + if(!uType) { + BfWriteByte(uMessage, 3); + BfWriteString(uMessage, szIdent); + } else { + PbSetInt(uMessage, "msg_dst", 3); + PbAddString(uMessage, "params", szIdent); + while(j < 5) { + PbAddString(uMessage, "params", NULL_STRING); + j++; + } + } + + EndMessage(); + } + } + } +} + +public Processing cc_proc_OnNewMessage(const int[] props, int propsCount, ArrayList params) { + static const char parentChannel[] = "TM"; + + char szBuffer[MESSAGE_LENGTH]; + params.GetString(0, szBuffer, sizeof(szBuffer)); + + currentObject = NULL_STRING; + + if(strcmp(szBuffer, parentChannel) != 0 || IsClientSourceTV(props[1])) + return Proc_Continue; + + // message object + params.GetString(2, szBuffer, sizeof(szBuffer)); + if(!jMessanger.HasKey(szBuffer)) + return Proc_Continue; + + FormatEx(currentObject, sizeof(currentObject), "%s", szBuffer); + + JsonObject msg = asJSONO(jMessanger.Get(szBuffer)); + int sender = GetClientOfUserId(msg.GetInt("userid")); + + jConfig.GetString("identificator", szBuffer, sizeof(szBuffer)); + params.SetString(0, szBuffer); + + msg.GetString("event", szBuffer, sizeof(szBuffer)); + + char reason[NAME_LENGTH] = "no reason"; + if(szBuffer[7] == 'd') + msg.GetString("reason", reason, sizeof(reason)); + + char network[NAME_LENGTH]; + msg.GetString("network", network, sizeof(network)); + + if(sender && IsClientConnected(sender) && jConfig.GetBool("useLog")) + LogAction( + sender, -1, "\"%L\" %s the game (%s). Network ID: %s", + sender, szBuffer[7] == 'd' ? "disconnected from" : "connected to", reason, network + ); + + delete msg; + return Proc_Change; +} + +public Processing cc_proc_OnRebuildString(const int[] props, int part, ArrayList params, int &level, char[] value, int size) { + static char szIdent[64]; + params.GetString(0, szIdent, sizeof(szIdent)); + + static char szBuffer[MESSAGE_LENGTH]; + jConfig.GetString("identificator", szBuffer, sizeof(szBuffer)); + + // LogMessage("Ident(now): %s, Ident(expected): %s, MessagerHas: %b", szIdent, szBuffer, jMessanger.HasKey(currentObject)); + + if(strcmp(szIdent, szBuffer) != 0 || !currentObject[0] || !jMessanger.HasKey(currentObject)) { + return Proc_Continue; + } + + int priority = jConfig.GetInt("priority"); + if(level > priority) + return Proc_Continue; + + level = priority; + // LogMessage("Level now: %d", level); + + JsonObject message = asJSONO(jMessanger.Get(currentObject)); + + static char event[NAME_LENGTH]; + message.GetString("event", event, sizeof(event)); + + static char szBot[NAME_LENGTH]; + FormatEx(szBot, sizeof(szBot), "%T", message.GetInt("bot") ? "js_bot" : "js_player", props[2]); + + static char name[NAME_LENGTH]; + message.GetString("name", name, sizeof(name)); + + static char network[NAME_LENGTH]; + message.GetString("network", network, sizeof(network)); + + static char buffer[MESSAGE_LENGTH]; + message.GetString(event[7] == 'd' ? "reason" : "address", buffer, sizeof(buffer)); + + delete message; + + JsonArray jValues; + + switch(part) { + case BIND_PROTOTYPE: { + if(jConfig.HasKey(szBinds[part]) && jConfig.GetString(szBinds[part], value, size)) { + Format(value, size, "%c %T", 1, value, props[2]); // because the template contains the body of the message, not the tag + } + } + + case BIND_STATUS, BIND_STATUS_CO, BIND_NAME_CO, BIND_MSG_CO, BIND_PREFIX_CO: { + if(jConfig.GetString(szBinds[part], value, size) && TranslationPhraseExists(value)) { + + if(part != BIND_STATUS) + Format(value, size, "%T", value, props[2]); + + else Format(value, size, "%T", value, props[2], szBot, network); + } + } + + case BIND_PREFIX: + { + if(jConfig.GetString(szBinds[part], value, size) && TranslationPhraseExists(value)) + Format(value, size, "%T", value, props[2]); + } + + case BIND_NAME: + FormatEx(value, size, "%s", name); + + case BIND_MSG: { + if(jConfig.HasKey(szBinds[part]) && (jValues = asJSONA(jConfig.Get(szBinds[part]))) && jValues.Size) { + jValues.GetString(event[7] == 'd', value, size); + + if(TranslationPhraseExists(value)) + Format(value, size, "%T", value, props[2], buffer); + } + } + } + + delete jValues; + return Proc_Change; +} diff --git a/ccp-jointeam/scripting/ccp-jointeam.sp b/ccp-jointeam/scripting/ccp-jointeam.sp new file mode 100644 index 0000000..bc1bf9d --- /dev/null +++ b/ccp-jointeam/scripting/ccp-jointeam.sp @@ -0,0 +1,268 @@ +#include + +#pragma newdecls required + +#include + +public Plugin myinfo = +{ + name = "[CCP] Join team", + author = "rej.chev?", + description = "...", + version = "1.1.0", + url = "https://discord.gg/cFZ97Mzrjy" +}; + +static const char pkgKey[] = "join_team"; + +static const char objectPattern[] = "joinTeam@%d"; + +int counter; + +JsonObject jConfig; +JsonObject jMessanger; + +bool g_bLate; + +char currentObject[64]; + +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { + #if defined DEBUG + DBUILD() + #endif + + g_bLate = late; + return APLRes_Success; +} + +public void OnPluginStart() { + // Temporary solution + LoadTranslations("ccp-jointeam.phrases"); + + HookEvent("player_team", EventTeam, EventHookMode_Pre); +} + +public void OnMapStart() { + #if defined DEBUG + DBUILD() + #endif + + // Handshake + cc_proc_APIHandShake(cc_get_APIKey()); + + // late load + if(g_bLate) { + g_bLate = false; + + for(int i; i <= MaxClients; i++) + if(Packager.GetPackage(i)) + pckg_OnPackageAvailable(i); + } + + counter = 0; + + delete jMessanger; + jMessanger = asJSONO(new Json("{}")); +} + +public void pckg_OnPackageAvailable(int iClient) { + // static const char cloud[] = "cloud"; + static char config[MESSAGE_LENGTH] = "configs/ccprocessor/join-team/settings.json"; + + if(iClient) + return; + + + if(jConfig) { + delete jConfig; + } + + // Loaded from cloud + // if(objPackage.HasKey(pkgKey) && objPackage.HasKey(cloud) && objPackage.GetBool(cloud)) { + // if(!iClient) { + // jConfig = asJSONO(objPackage.Get(pkgKey)); + // } + + // return; + // } + + // Load from local + if(config[0] == 'c') { + BuildPath(Path_SM, config, sizeof(config), config); + } + + if(!FileExists(config)) { + SetFailState("Config file is not exists: %s", config); + } + + jConfig = asJSONO(Json.JsonF(config, 0)); + + Packager.GetPackage(iClient).SetArtifact(pkgKey, jConfig); +} + +public void pckg_OnPackageUpdated(Handle plugin, int iClient) { + if(iClient) + return; + + char szBuffer[MAX_NAME_LENGTH]; + GetPluginFilename(plugin, szBuffer, sizeof(szBuffer)); + + if(strcmp(szBuffer, "ccp-channel-mgr.smx")) + return; + + char szMyself[MAX_NAME_LENGTH]; + jConfig.GetString("identificator", szMyself, sizeof(szMyself)); + + if(ccp_FindChannel(szMyself) == -1) + ccp_AddChannel(szMyself); + +} + +Action EventTeam(Event event, const char[] name, bool dbc) { + if(!jConfig.GetBool("processing")) { + return Plugin_Continue; + } + + event.SetBool("silent", true); + + if(event.GetInt("disconnect") || jConfig.GetBool("hideMessages")) { + return Plugin_Changed; + } + + int userId; + userId = event.GetInt("userid"); + + int iTeam; + iTeam = event.GetInt("team"); + + JsonArray teamVisibility = asJSONA(jConfig.Get("hide")); + if(teamVisibility.Size > iTeam && iTeam >= 0 && teamVisibility.GetBool(iTeam)) { + delete teamVisibility; + return Plugin_Changed; + } + + delete teamVisibility; + + char szName[NAME_LENGTH]; + GetClientName(GetClientOfUserId(userId), szName, sizeof(szName)); + + JsonObject message = asJSONO(new Json("{}")); + message.SetInt("userId", userId); + message.SetInt("team", iTeam); + message.SetString("userName", szName); + + FormatEx(szName, sizeof(szName), objectPattern, counter++); + + jMessanger.Set(szName, message); + delete message; + + for(int i = 1; i <= MaxClients; i++) { + if(IsClientInGame(i)) { + PrintToChat(i, szName); + } + } + + return Plugin_Changed; +} + +public Processing cc_proc_OnNewMessage(const int[] props, int propsCount, ArrayList params) { + static const char parentChannel[] = "TM"; + + char szBuffer[MESSAGE_LENGTH]; + params.GetString(0, szBuffer, sizeof(szBuffer)); + + currentObject = NULL_STRING; + + if(strcmp(szBuffer, parentChannel) != 0 || IsClientSourceTV(props[1])) { + return Proc_Continue; + } + + // message object + params.GetString(2, szBuffer, sizeof(szBuffer)); + if(!jMessanger.HasKey(szBuffer)) { + return Proc_Continue; + } + + FormatEx(currentObject, sizeof(currentObject), "%s", szBuffer); + + JsonObject msg = asJSONO(jMessanger.Get(szBuffer)); + int sender = GetClientOfUserId(msg.GetInt("userId")); + + jConfig.GetString("identificator", szBuffer, sizeof(szBuffer)); + params.SetString(0, szBuffer); + + if(sender && IsClientConnected(sender) && jConfig.GetBool("useLog")) { + LogAction(sender, -1, "\"%L\" connected to %d team", sender, msg.GetInt("team")); + } + + delete msg; + return Proc_Change; +} + +public Processing cc_proc_OnRebuildString(const int[] props, int part, ArrayList params, int &level, char[] value, int size) { + static char szIdent[64]; + params.GetString(0, szIdent, sizeof(szIdent)); + + static char szBuffer[MESSAGE_LENGTH]; + jConfig.GetString("identificator", szBuffer, sizeof(szBuffer)); + + // LogMessage("Ident(now): %s, Ident(expected): %s, MessagerHas: %b", szIdent, szBuffer, jMessanger.HasKey(currentObject)); + + if(strcmp(szIdent, szBuffer) != 0 || !currentObject[0] || !jMessanger.HasKey(currentObject)) { + return Proc_Continue; + } + + int priority = jConfig.GetInt("priority"); + if(level > priority) + return Proc_Continue; + + level = priority; + // LogMessage("Level now: %d", level); + + JsonObject message = asJSONO(jMessanger.Get(currentObject)); + + static int team; + team = message.GetInt("team"); + + static char userName[NAME_LENGTH]; + message.GetString("userName", userName, sizeof(userName)); + + delete message; + + JsonArray jValues; + + switch(part) { + case BIND_PROTOTYPE: { + if(jConfig.HasKey(szBinds[part]) && jConfig.GetString(szBinds[part], value, size)) { + Format(value, size, "%c %T", 1, value, props[2]); // because the template contains the body of the message, not the tag + } + } + + case BIND_STATUS, BIND_STATUS_CO: { + if(jConfig.HasKey(szBinds[part]) && (jValues = asJSONA(jConfig.Get(szBinds[part]))) && jValues.Size) { + jValues.GetString(1, value, size); // server is always alive :/ + Format(value, size, "%T", value, props[2]); + } + } + + case BIND_TEAM, BIND_TEAM_CO: { + if(jConfig.HasKey(szBinds[part]) && (jValues = asJSONA(jConfig.Get(szBinds[part]))) && jValues.Size) { + jValues.GetString(team, value, size); + Format(value, size, "%T", value, props[2]); + } + } + + case BIND_NAME: { + FormatEx(value, size, "%s", userName); + } + + default: { + if(jConfig.HasKey(szBinds[part]) && jConfig.GetString(szBinds[part], value, size)) { + FormatEx(value, size, "%T", value, props[2]); + } + } + } + + delete jValues; + return Proc_Change; +} diff --git a/src/ccp-radiomsg.sp b/ccp-radiomsg/scripting/ccp-radiomsg.sp similarity index 100% rename from src/ccp-radiomsg.sp rename to ccp-radiomsg/scripting/ccp-radiomsg.sp diff --git a/src/ccp-saytext.sp b/ccp-saytext/scripting/ccp-saytext.sp similarity index 100% rename from src/ccp-saytext.sp rename to ccp-saytext/scripting/ccp-saytext.sp diff --git a/src/ccp-saytext2.sp b/ccp-saytext2/scripting/ccp-saytext2.sp similarity index 100% rename from src/ccp-saytext2.sp rename to ccp-saytext2/scripting/ccp-saytext2.sp diff --git a/ccp-schat/scripting/ccp-schat.sp b/ccp-schat/scripting/ccp-schat.sp new file mode 100644 index 0000000..3a2c353 --- /dev/null +++ b/ccp-schat/scripting/ccp-schat.sp @@ -0,0 +1,344 @@ +#pragma newdecls required + +#include +#include +#include + +#define SZ(%0) %0, sizeof(%0) + +public Plugin myinfo = +{ + name = "[CCP] Custom Chat ", + author = "rej.chev?", + description = "...", + version = "2.0.0", + url = "discord.gg/ChTyPUG" +}; + +const ItemType g_IType = Item_Togglable; + +static const char pkgKey[] = "shop_chat"; + +int levels[BIND_MAX]; + +bool g_bLate; + +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { + g_bLate = late; + return APLRes_Success; +} + +public void OnPluginStart() +{ + LoadTranslations("ccp_core.phrases"); + LoadTranslations("ccp_shop.phrases"); + + manageConVars(); +} + +void manageConVars(bool bCreate = true) { + char szBuffer[128]; + + for(int i; i < BIND_MAX; i++) { + FormatBind("ccp_shop_", i, 'l', szBuffer, sizeof(szBuffer)/2); + if(bCreate){ + Format(szBuffer[strlen(szBuffer)+1], sizeof(szBuffer), "Priority level for %s", szBinds[i]); + CreateConVar(szBuffer, "1", szBuffer[strlen(szBuffer)+1], _, true, 1.0).AddChangeHook(onChange); + } else { + onChange(FindConVar(szBuffer), NULL_STRING, NULL_STRING); + } + } + + if(bCreate) { + AutoExecConfig(true, "ccp_shopchat", "ccprocessor"); + } +} + +public void onChange(ConVar convar, const char[] oldVal, const char[] newVal) +{ + char szBuffer[64]; + convar.GetName(szBuffer, sizeof(szBuffer)); + + int part = BindFromString(szBuffer); + if(part == BIND_MAX) + return; + + levels[part] = convar.IntValue; +} + +public void pckg_OnPackageAvailable(int iClient) { + static char config[MESSAGE_LENGTH] = "configs/shop/ccprocessor/shop_chat.json"; + + Package pack = Packager.GetPackage(iClient); + + if(!iClient) { + + + if(config[0] == 'c') + BuildPath(Path_SM, config, sizeof(config), config); + + if(!FileExists(config)) + SetFailState("Config file is not exists: %s", config); + } + + pack.SetArtifact(pkgKey, (!iClient) ? Json.JsonF(config, 0) : new Json("{}"), freeAnyway); +} + +public void OnMapStart() { + // Handshake + cc_proc_APIHandShake(cc_get_APIKey()); + + manageConVars(false); + + // late load + if(g_bLate) { + g_bLate = false; + + for(int i = 1; i <= MaxClients; i++) + if(Packager.GetPackage(i)) + pckg_OnPackageAvailable(i); + } + + if(Shop_IsStarted()) + Shop_Started(); +} + +public void Shop_Started() +{ + OnMapEnd(); + RegisterCategorys(); +} + +public void OnMapEnd() +{ + Shop_UnregisterMe(); +} + +public void OnPluginEnd() +{ + OnMapEnd(); +} + +void RegisterCategorys() { + JsonObject obj; + if(!(obj = asJSONO(Packager.GetPackage(0).GetArtifact(pkgKey)))) + return; + + JsonArray jsonPart; + JsonObject item; + + CategoryId id; + + for(int i; i < BIND_MAX; i++ ) { + + if(!obj.HasKey(szBinds[i])) + continue; + + jsonPart = asJSONA(obj.Get(szBinds[i])); + if(!jsonPart || !jsonPart.Size) { + delete jsonPart; + continue; + } + + id = Shop_RegisterCategory( + szBinds[i], szBinds[i], NULL_STRING, OnCategoryDisplayed + ); + + if(id == INVALID_CATEGORY) { + delete jsonPart; + continue; + } + + for(int j; j < jsonPart.Size; j++) { + if((item = asJSONO(jsonPart.Get(j)))) + RegisterItem(id, item); + + delete item; + } + + delete jsonPart; + } + + delete obj; +} + +void RegisterItem(CategoryId cid, JsonObject item) +{ + char szBuffer[NAME_LENGTH]; + item.GetString("value", szBuffer, sizeof(szBuffer)); + + if(Shop_StartItem(cid, szBuffer)) + { + Shop_SetInfo(szBuffer, NULL_STRING, item.GetInt("price"), item.GetInt("sellprice"), g_IType, item.GetInt("duration")); + Shop_SetCallbacks(OnItemRegistered, OnItemToogle, _, OnItemDisplay, _, _, OnItemBuy, OnItemSell); + + Shop_EndItem(); + } +} + +public void OnItemRegistered(CategoryId category_id, const char[] category, const char[] item, ItemId item_id) +{ + // ... +} + +public ShopAction OnItemToogle(int iClient, CategoryId category_id, const char[] category, ItemId item_id, const char[] item, bool isOn, bool elapsed) +{ + Package pack; + JsonObject obj; + if((pack = Packager.GetPackage(iClient))) + obj = asJSONO(pack.GetArtifact(pkgKey)); + + char szValue[MESSAGE_LENGTH]; + int part = BindFromString(category); + + if(obj && IsPartValid(obj, part)) + obj.GetString(szBinds[part], szValue, sizeof(szValue)); + + if(!obj) + obj = asJSONO(new Json("{}")); + + if(szValue[0]) + { + if(!StrEqual(item, szValue)) + Shop_ToggleClientItem(iClient, Shop_GetItemId(category_id, szValue), Toggle_Off); + + szValue = NULL_STRING; + } + + if(!(isOn || elapsed)) + strcopy(szValue, sizeof(szValue), item); + + if(szValue[0]) { + obj.SetString(szBinds[part], szValue); + } else obj.Set(szBinds[part], null); + + pack.SetArtifact(pkgKey, obj, freeAnyway); + + return (isOn || elapsed) ? Shop_UseOff : Shop_UseOn; +} + +public bool OnItemDisplay(int client, CategoryId category_id, const char[] category, ItemId item_id, const char[] item, ShopMenu menu, bool &disabled, const char[] name, char[] buffer, int maxlen) +{ + int part = BindFromString(category); + + FormatEx(buffer, maxlen, "%T", name, client); + + ccp_replaceColors(buffer, true); + + if(part == BIND_PREFIX) + Format(buffer, maxlen, "%T", "tag_display", client, buffer); + + return true; +} + +public bool OnCategoryDisplayed(int client, CategoryId category_id, const char[] category, const char[] name, char[] buffer, int maxlen, ShopMenu menu) +{ + FormatEx(buffer, maxlen, "%T", name, client); + return true; +} + +public bool OnItemBuy(int client, CategoryId category_id, const char[] category, ItemId item_id, const char[] item, ItemType type, int price, int sell_price, int value) { + return true; +} + +public bool OnItemSell(int client, CategoryId category_id, const char[] category, ItemId item_id, const char[] item, ItemType type, int sell_price) +{ + Package pack; + if(!(pack = Packager.GetPackage(client))) + return false; + + char szValue[MESSAGE_LENGTH]; + int part = BindFromString(category); + + JsonObject obj; + if(!(obj = asJSONO(pack.GetArtifact(pkgKey)))) + return false; + + if(IsPartValid(obj, part)) + obj.GetString(szBinds[part], szValue, sizeof(szValue)); + + if(szValue[0] && StrEqual(szValue, item, false)) + obj.Remove(szBinds[part]); + + return pack.SetArtifact(pkgKey, obj, freeAnyway); +} + +JsonObject objModel; + +public Processing cc_proc_OnRebuildString(const int[] props, int part, ArrayList params, int &level, char[] value, int size) { + if(!SENDER_INDEX(props[1]) || level > levels[part]) { + return Proc_Continue; + } + + Package serverPack; + if(!(serverPack = Packager.GetPackage(0))) + return Proc_Continue; + + Package clientPack; + if(!(clientPack = Packager.GetPackage(SENDER_INDEX(props[1])))) + return Proc_Continue; + + objModel = asJSONO(serverPack.GetArtifact(pkgKey)); + if(!objModel.HasKey("channels")) { + delete objModel; + return Proc_Continue; + } + + JsonArray channels = asJSONA(objModel.Get("channels", true)); + + char szIndent[64]; + params.GetString(0, szIndent, sizeof(szIndent)); + if(!JsonArrayContainsString(channels, szIndent, true)) { + delete channels; + return Proc_Continue; + } + + delete channels; + + objModel = asJSONO(clientPack.GetArtifact(pkgKey)); + if(!objModel || !IsPartValid(objModel, part)) { + delete objModel; + return Proc_Continue; + } + + static char szValue[MESSAGE_LENGTH]; + if(!objModel.GetString(szBinds[part], szValue, sizeof(szValue)) || !szValue[0]) { + delete objModel; + return Proc_Continue; + } + + if(part == BIND_PREFIX && TranslationPhraseExists(szValue)) { + Format(szValue, sizeof(szValue), "%T", szValue, props[2]); + } + + level = levels[part]; + FormatEx(value, size, szValue); + + delete objModel; + return Proc_Change; +} + +stock bool IsPartValid(JsonObject model, int part) { + + JsonType type = model.GetType(szBinds[part]); + + return type != JInvalidType && type != JNullType; +} + +stock bool JsonArrayContainsString(const JsonArray array, const char[] str, bool casesens = true) { + + if(!array) + return false; + + char buffer[512]; + for(int i = 0; i < array.Size; i++) { + + if(!array.GetString(i, buffer, sizeof(buffer))) + continue; + + if(!strcmp(str, buffer, casesens)) + return true; + } + + return false; +} \ No newline at end of file diff --git a/ccp-spacecleaner/scripting/ccp-spacecleaner.sp b/ccp-spacecleaner/scripting/ccp-spacecleaner.sp new file mode 100644 index 0000000..9a46b67 --- /dev/null +++ b/ccp-spacecleaner/scripting/ccp-spacecleaner.sp @@ -0,0 +1,55 @@ +#pragma newdecls required + +#include +#include + +public Plugin myinfo = +{ + name = "[CCP] Space Messages", + author = "nullent?", + description = "...", + version = "2.0.0", + url = "discord.gg/ChTyPUG" +}; + +JsonObject objConfig; + +public void OnMapStart() { + cc_proc_APIHandShake(cc_get_APIKey()); + + static char szBuffer[MESSAGE_LENGTH] = "configs/ccprocessor/space-msgs/settings.json"; + if(szBuffer[0] == 'c') { + BuildPath(Path_SM, szBuffer, sizeof(szBuffer), szBuffer); + } else if(!FileExists(szBuffer)) { + SetFailState("Where is my config: %s", szBuffer); + } + + delete objConfig; + objConfig = asJSONO(Json.JsonF(szBuffer, 0)); +} + +public Processing cc_proc_OnRebuildString_Post(const int[] props, int part, ArrayList params, int level, const char[] value) { + if(part != BIND_MSG) { + return Proc_Continue; + } + + char szIndent[64]; + params.GetString(0, szIndent, sizeof(szIndent)); + if(!objConfig.HasKey(szIndent)) { + LogMessage("'%s' channel skipped. Add this channel to: configs/ccprocessor/space-msgs/settings.json", szIndent); + return Proc_Continue; + } + + if(!objConfig.GetBool(szIndent)) { + for(int i; i < strlen(value); i++) { + if(value[i] >= 33) { + return Proc_Continue; + } + } + + return Proc_Stop; + } + + return Proc_Continue; +} + diff --git a/ccp-synchronizer/scripting/ccp-synchronizer.sp b/ccp-synchronizer/scripting/ccp-synchronizer.sp new file mode 100644 index 0000000..05bffc8 --- /dev/null +++ b/ccp-synchronizer/scripting/ccp-synchronizer.sp @@ -0,0 +1,170 @@ +#pragma newdecls required + +// jansson | packager | ccprocessor +#include + +#include +#include + +public Plugin myinfo = +{ + name = "[CCP] Synchronizer", + author = "rej.chev?", + description = "...", + version = "2.0.0", + url = "https://discord.gg/cFZ97Mzrjy" +}; + +static const char pkgKey[] = "synchronizer"; + +bool g_bLate; + +int nextSyncTick; + +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { + #if defined DEBUG + DBUILD() + #endif + + g_bLate = late; + return APLRes_Success; +} + +public void OnPluginStart() { + LoadTranslations("admin-channel.phrases"); +} + +public void OnMapStart() { + #if defined DEBUG + DBUILD() + #endif + + // Handshake + cc_proc_APIHandShake(cc_get_APIKey()); + + // late load + if(g_bLate) { + g_bLate = false; + + for(int i; i <= MaxClients; i++) + if(Packager.GetPackage(i)) + pckg_OnPackageAvailable(i); + } + + nextSyncTick = GetGameTickCount(); + + SDKHook(GetPlayerResourceEntity(), SDKHook_ThinkPost, OnThinkPost); +} + +public void OnThinkPost(int ent) { + if(ent == -1) { + SDKUnhook(ent, SDKHook_ThinkPost, OnThinkPost); + return; + } + + static Package pck; + if(!pck && !(pck = Packager.GetPackage(0))) + return; + + static int currentTick; + currentTick = GetGameTickCount(); + + static char szBuffer[4]; + + if(currentTick >= nextSyncTick) { + JsonObject obj; + + if(!pck.HasArtifact(pkgKey)) + return; + + obj = asJSONO(pck.GetArtifact(pkgKey)); + + nextSyncTick = currentTick + obj.GetInt("delay") * RoundFloat(getTickrate()); + + delete obj; + + JsonArray channels; + if(!(channels = asJSONA(ccp_GetChannelList(0)))) + return; + + ArrayList arr = new ArrayList(MAX_LENGTH, 0); + static char channel[PREFIX_LENGTH]; + + for(int i = 1, a; i <= MaxClients; i++) { + if(IsClientInGame(i)) { + + a = i<<3|GetClientTeam(i)<<1|view_as(IsPlayerAlive(i)); + + for(int j; j < channels.Size; j++) { + channels.GetString(j, channel, sizeof(channel)); + + stock_RebuildMsg(arr, -1, a, i, channel, NULL_STRING, szBuffer, szBuffer, szBuffer); + } + } + } + + delete arr; + delete channels; + } +} + +public void pckg_OnPackageAvailable(int iClient) { + + static char config[MESSAGE_LENGTH] + = "configs/ccprocessor/synchronizer/settings.json"; + + Package pack = Packager.GetPackage(iClient); + + if(!iClient) { + + // Load from local + if(config[0] == 'c') + BuildPath(Path_SM, config, sizeof(config), config); + + if(!FileExists(config)) + SetFailState("Config file is not exists: %s", config); + } + + if(!pack.SetArtifact(pkgKey, (!iClient) ? Json.JsonF(config) : new Json("{}"), freeAnyway)) + SetFailState("Something went wrong on set artifact '%s' for client %d", pkgKey, iClient); +} + + +public Processing cc_proc_OnRebuildString_Post(const int[] props, int part, ArrayList params, int level, const char[] value) { + Package pack; + if(!(pack = Packager.GetPackage(SENDER_INDEX(props[1])))) + return Proc_Continue; + + char szBuffer[64]; + params.GetString(1, szBuffer, sizeof(szBuffer)); + + JsonObject obj; + + // this is not the pseudo call ;/ + if(szBuffer[0]) + return Proc_Continue; + + params.GetString(0, szBuffer, sizeof(szBuffer)); + + if(!(obj = asJSONO(pack.GetArtifact(pkgKey)))) + return Proc_Continue; + + JsonObject ch; + + ch = !obj.HasKey(szBuffer) + ? asJSONO(new Json("{}")) + : asJSONO(obj.Get(szBuffer)); + + ch.SetString(szBinds[part], value); + + obj.Set(szBuffer, ch); + delete ch; + + pack.SetArtifact(pkgKey, obj, freeAnyway); + + return Proc_Continue; +} + +stock float getTickrate() { + return (1/GetTickInterval()); +} \ No newline at end of file diff --git a/ccp-tchanger/scripting/ccp-tchanger.sp b/ccp-tchanger/scripting/ccp-tchanger.sp new file mode 100644 index 0000000..4763458 --- /dev/null +++ b/ccp-tchanger/scripting/ccp-tchanger.sp @@ -0,0 +1,191 @@ + +#include + +#pragma newdecls required + +#include +#include + +public Plugin myinfo = +{ + name = "[CCP] Tag changer", + author = "rej.chev...", + description = "...", + version = "2.0.0", + url = "discord.gg/ChTyPUG" +}; + +bool g_bLate; + +static const char pkgKey[] = "tag_changer"; + +public void OnMapStart() { + #if defined DEBUG + DBUILD() + #endif + + // Handshake + cc_proc_APIHandShake(cc_get_APIKey()); + + // late load + if(g_bLate) { + g_bLate = false; + + for(int i; i <= MaxClients; i++) + if(Packager.GetPackage(i)) + pckg_OnPackageAvailable(i); + } +} + +public void pckg_OnPackageAvailable(int iClient) { + static char config[MESSAGE_LENGTH] = "configs/ccprocessor/tag-changer/settings.json"; + + if(iClient) + return; + + // Load from local + if(config[0] == 'c') + BuildPath(Path_SM, config, sizeof(config), config); + + if(!FileExists(config)) + SetFailState("Config file is not exists: %s", config); + + Packager.GetPackage(iClient).SetArtifact(pkgKey, Json.JsonF(config, 0), freeAnyway); +} + +public Processing cc_proc_OnRebuildString(const int[] props, int part, ArrayList params, int &level, char[] value, int size) { + if(part != BIND_PREFIX && part != BIND_MSG || !value[0]) + return Proc_Continue; + + char szBuffer[MAX_LENGTH]; + params.GetString(0, szBuffer, sizeof(szBuffer)); + + if(!ChannelExists(szBuffer)) + return Proc_Continue; + + if(level > GetReplacementLevel()) + return Proc_Continue; + + level = GetReplacementLevel(); + + return EditedValue(value, size); +} + +int GetReplacementLevel() { + JsonObject jsObject; + if((jsObject = asJSONO(Packager.GetPackage(0).GetArtifact(pkgKey))) == null) + return -1; + + int level = jsObject.GetInt("level"); + + delete jsObject; + return level; +} + +bool ChannelExists(const char[] channel) { + JsonObject jsObject; + if(!(jsObject = asJSONO(Packager.GetPackage(0).GetArtifact(pkgKey)))) + return true; + + JsonArray array; + if(!(array = asJSONA(jsObject.Get("channels", true)))) { + delete jsObject; + return true; + } + + bool exists = JsonArrayContainsString(array, channel); + + delete array; + return exists; +} + +Processing EditedValue(char[] value, int size) { + char szBuffer[MAX_LENGTH]; + FormatEx(szBuffer, sizeof(szBuffer), "%s", value); + + TrimString(szBuffer); + + // LogMessage("Preprocessing: %s | %s", szBuffer, value); + + ProcessingValue(szBuffer, sizeof(szBuffer)); + + // LogMessage("PostProcessing: %s | %s", szBuffer, value); + + bool cmp; + if((cmp = UTF8strcmp(value, szBuffer, false) != 0)) + FormatEx(value, size, "%s", szBuffer); + + return cmp ? Proc_Change : Proc_Continue; +} + +void ProcessingValue(char[] szBuffer, int size) { + JsonObject jsObject; + if((jsObject = asJSONO(Packager.GetPackage(0).GetArtifact(pkgKey))) == null) + return; + + JsonObject tags; + if((tags = asJSONO(jsObject.Get("tags", true))) == null) { + delete jsObject; + return; + } + + JsonArray keys; + if(!(keys = tags.Keys(JStringType)) || !keys.Size) + return; + + int s, e; + char szKey[NAME_LENGTH]; + char szValue[PREFIX_LENGTH]; + for(int i = 0; i < keys.Size; i++) { + s = e = 0; + + if(!tags.GetString(szKey, szValue, sizeof(szValue))) + continue; + + if((s = UTF8StrContains(szBuffer, szKey, false)) != -1 && !s && IsCtxEquals(szBuffer, szKey, s, e)) { + RemoveString(szBuffer, size, s, s+e); + Format(szBuffer, size, "%s%s", szValue, szBuffer); + } + + // LogMessage("Find: s-%d | e-%d | %s", s, e, szKey); + } +} + +bool IsCtxEquals(const char[] message, const char[] input, int &s, int &e) { + if(!message[0] || !input[0] || strlen(message) < strlen(input)) + return false; + + while(e < strlen(input)) { + if(message[e] != input[e]) + return false; + + e++; + } + + return true; +} + +stock void RemoveString(char[] buffer, int size, int s, int e) { + if(!buffer[0] || s == -1 || e == -1) + return; + + strcopy(buffer[s], size, buffer[e]); +} + +stock bool JsonArrayContainsString(const JsonArray array, const char[] str, bool casesens = true) { + + if(!array) + return false; + + char buffer[512]; + for(int i = 0; i < array.Size; i++) { + + if(!array.GetString(i, buffer, sizeof(buffer))) + continue; + + if(!strcmp(str, buffer, casesens)) + return true; + } + + return false; +} \ No newline at end of file diff --git a/ccp-tcolor/scripting/ccp-tcolor.sp b/ccp-tcolor/scripting/ccp-tcolor.sp new file mode 100644 index 0000000..a3d15c6 --- /dev/null +++ b/ccp-tcolor/scripting/ccp-tcolor.sp @@ -0,0 +1,114 @@ +#pragma newdecls required + +#include + +#include +#include + +int m_iCompTeammateColor = -1; + +int ColorArray[MAXPLAYERS+1]; + +bool IsMapEnd, EnColor; + +char szStatusSmb[STATUS_LENGTH]; +int Level; + +public Plugin myinfo = +{ + name = "[CCP] Teammate color ", + author = "nullent?", + description = "Competitve teammate color into chat", + version = "1.5.2", + url = "discord.gg/ChTyPUG" +}; + + +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) +{ + return (GetEngineVersion() != Engine_CSGO /*|| FindConVar("game_mode").IntValue != 1*/) ? APLRes_SilentFailure : APLRes_Success; +} + +public void OnPluginStart() +{ + m_iCompTeammateColor = FindSendPropInfo("CCSPlayerResource", "m_iCompTeammateColor"); + + CreateConVar("cmarker_priority_level", "1", "Replacement Priority", _, true, 0.0).AddChangeHook(OnLevelChanged); + CreateConVar("cmarker_status_symbol", "●", "Any character").AddChangeHook(OnSymbolChanged); + CreateConVar("cmarker_added_color", "1", "Enable/Disable color").AddChangeHook(OnColorChanged); + + AutoExecConfig(true, "cmarker_comp", "ccprocessor"); +} + +public void OnLevelChanged(ConVar cvar, const char[] szOldVal, const char[] szNewVal) +{ + Level = cvar.IntValue; +} + +public void OnColorChanged(ConVar cvar, const char[] szOldVal, const char[] szNewVal) +{ + EnColor = cvar.BoolValue; +} + +public void OnSymbolChanged(ConVar cvar, const char[] szOldVal, const char[] szNewVal) +{ + cvar.GetString(szStatusSmb, sizeof(szStatusSmb)); +} + +public void OnMapStart() +{ + cc_proc_APIHandShake(cc_get_APIKey()); + + OnLevelChanged(FindConVar("cmarker_priority_level"), NULL_STRING, NULL_STRING); + OnSymbolChanged(FindConVar("cmarker_status_symbol"), NULL_STRING, NULL_STRING); + OnColorChanged(FindConVar("cmarker_added_color"), NULL_STRING, NULL_STRING); + + IsMapEnd = false; + + SDKHook(GetPlayerResourceEntity(), SDKHook_ThinkPost, OnThinkPost); +} + +public void OnMapEnd() +{ + IsMapEnd = true; +} + +public void OnThinkPost(int entity) +{ + if(entity == -1) + return; + + else if(IsMapEnd) + SDKUnhook(entity, SDKHook_ThinkPost, OnThinkPost); + + GetEntDataArray(entity, m_iCompTeammateColor, ColorArray, sizeof(ColorArray)); +} + +public Processing cc_proc_OnRebuildString(const int[] props, int part, ArrayList params, int &level, char[] value, int size) { + char szIndent[64]; + params.GetString(0, szIndent, sizeof(szIndent)); + + if((szIndent[0] != 'S' && szIndent[1] != 'T' && strlen(szIndent) < 3) + || (part != BIND_STATUS && part != BIND_STATUS_CO) + || level > Level) + return Proc_Continue; + + if((part == BIND_STATUS && !szStatusSmb[0]) || (part == BIND_STATUS_CO && !EnColor)) + return Proc_Continue; + + level = Level; + + if(part == BIND_STATUS) + FormatEx(value, size, szStatusSmb); + + else FormatEx(value, size, "%c", GetColor(SENDER_INDEX(props[1]))); + + return Proc_Change; +} + +int GetColor(int iClient) +{ + static const int colors[] = {9, 14, 4, 11, 16}; + + return (ColorArray[iClient] >= sizeof(colors) || ColorArray[iClient] < 0) ? 1 : colors[ColorArray[iClient]]; +} diff --git a/src/ccp-textmsg.sp b/ccp-textmsg/scripting/ccp-textmsg.sp similarity index 100% rename from src/ccp-textmsg.sp rename to ccp-textmsg/scripting/ccp-textmsg.sp diff --git a/ccp-vchat/scripting/ccp-vchat.sp b/ccp-vchat/scripting/ccp-vchat.sp new file mode 100644 index 0000000..7ab1aa6 --- /dev/null +++ b/ccp-vchat/scripting/ccp-vchat.sp @@ -0,0 +1,503 @@ +#pragma newdecls required + +#include +#include +#include +#include + +public Plugin myinfo = +{ + name = "[CCP] Custom Chat ", + author = "rej.chev?", + description = "...", + version = "3.0.0", + url = "discord.gg/ChTyPUG" +}; + +ArrayList g_mPalette; + +int level[BIND_MAX]; + +int g_iBindNow[MAXPLAYERS+1]; + +bool g_bLate; + +Cookie cookie; + +static const char pkgKey[] = "vip_chat"; +static const char FEATURE[] = "ccp_chat"; + +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) +{ + g_bLate = late; + return APLRes_Success +} + +public void OnPluginStart() +{ + LoadTranslations("ccp_core.phrases"); + LoadTranslations("vip_ccpchat.phrases"); + LoadTranslations("vip_modules.phrases"); + + cookie = new Cookie(pkgKey, "", CookieAccess_Private); + + manageConVars(); + + if(VIP_IsVIPLoaded()) + VIP_OnVIPLoaded(); + + if(g_bLate) { + cc_config_parsed(); + pckg_OnPackageAvailable(0); + + for(int i = 1; i <= MaxClients; i++) { + OnClientPutInServer(i); + if(IsClientInGame(i) && !IsFakeClient(i) && VIP_IsClientVIP(i)) { + VIP_OnVIPClientLoaded(i); + } + } + + g_bLate = false; + } +} + +void manageConVars(bool bCreate = true) { + char szBuffer[128]; + + for(int i; i < BIND_MAX; i++) { + FormatBind(FEATURE, i, 'l', szBuffer, sizeof(szBuffer)/2); + if(bCreate){ + Format(szBuffer[strlen(szBuffer)+1], sizeof(szBuffer), "Priority level for %s", szBinds[i]); + CreateConVar(szBuffer, "1", szBuffer[strlen(szBuffer)+1], _, true, 1.0).AddChangeHook(onChange); + } else { + onChange(FindConVar(szBuffer), NULL_STRING, NULL_STRING); + } + } + + if(bCreate) { + AutoExecConfig(true, "ccp_vipchat", "ccprocessor"); + } +} + +public void onChange(ConVar convar, const char[] oldVal, const char[] newVal) +{ + char szBuffer[64]; + convar.GetName(szBuffer, sizeof(szBuffer)); + + int part = BindFromString(szBuffer); + if(part == BIND_MAX) + return; + + level[part] = convar.IntValue; +} + +public void OnMapStart() +{ + cc_proc_APIHandShake(cc_get_APIKey()); + + manageConVars(false); +} + +public void pckg_OnPackageAvailable(int iClient) { + if(iClient) + return; + + static char szConfig[PLATFORM_MAX_PATH] = "data/vip/modules/chat.json"; + + if(szConfig[0] == 'd') + BuildPath(Path_SM, szConfig, sizeof(szConfig), szConfig); + + if(!FileExists(szConfig)) + SetFailState("Where is my config??: %s", szConfig); + + Packager.GetPackage(iClient).SetArtifact(pkgKey, Json.JsonF(szConfig, 0), freeAnyway); +} + +public void cc_config_parsed() +{ + g_mPalette = cc_drop_palette(); +} + +public void VIP_OnVIPLoaded() +{ + // OnPluginEnd(); + VIP_RegisterFeature(FEATURE, BOOL, SELECTABLE, OnSelected_Feature); +} + +public void OnPluginEnd() +{ + if(!CanTestFeatures() + || GetFeatureStatus(FeatureType_Native, "VIP_UnregisterMe") != FeatureStatus_Available) + return; + + VIP_UnregisterMe(); +} + +public bool OnSelected_Feature(int iClient, const char[] szFeature) { + partsOfMsgsMenu(iClient).Display(iClient, MENU_TIME_FOREVER); + return false; +} + +public void VIP_OnVIPClientLoaded(int iClient) +{ + Json storage; + + char buffer[512]; + cookie.Get(iClient, buffer, sizeof(buffer)); + + if(!strlen(buffer) || !(storage = new Json(buffer))) + storage = new Json("{}"); + + Packager.GetPackage(iClient).SetArtifact(pkgKey, storage, freeAnyway); +} + +Menu partsOfMsgsMenu(int iClient) { + g_iBindNow[iClient] = BIND_MAX; + + Menu menu; + + char szBuffer[MESSAGE_LENGTH]; + VIP_GetClientVIPGroup(iClient, szBuffer, sizeof(szBuffer)); + + JsonObject artifact = asJSONO(Packager.GetPackage(0).GetArtifact(pkgKey)); + JsonObject group; + + if(!(group = asJSONO(artifact.Get(szBuffer, true)))) + delete artifact; + + JsonArray keys = group.Keys(JStringType); + + delete group; + + menu = new Menu(partsOfMsgsMenu_CallBack); + menu.SetTitle("%T %T \n \n", "parts_ofMsg_title_tag", iClient, "parts_ofMsg_title", iClient); + + group = asJSONO(Packager.GetPackage(iClient).GetArtifact(pkgKey)); + + char szValue[MESSAGE_LENGTH], out[MESSAGE_LENGTH]; + for(int i = 0; i < keys.Size; i++) { + keys.GetString(i, szBuffer, sizeof(szBuffer)); + + szValue = NULL_STRING; + + if(group.HasKey(szBuffer)) + group.GetString(szBuffer, szValue, sizeof(szValue)); + + if(!szValue[0]) + FormatEx(szValue, sizeof(szValue), "%T", "empty_value", iClient); + + if(TranslationPhraseExists(szValue)) + Format(szValue, sizeof(szValue), "%T", szValue, iClient); + + ccp_replaceColors(szValue, true); + + FormatEx(out, sizeof(out), "%c%T [%s]", BindFromString(szBuffer) + 1, szBuffer, iClient, szValue); + + menu.AddItem(out, out[1]); + } + + menu.ExitButton = true; + menu.ExitBackButton = true; + + if(!menu.ItemCount) + delete menu; + + delete keys; + delete group; + + return menu; +} + +public int partsOfMsgsMenu_CallBack(Menu hMenu, MenuAction action, int iClient, int iOpt2) +{ + switch(action) + { + case MenuAction_End: delete hMenu; + case MenuAction_Cancel: + { + if(iOpt2 == MenuCancel_ExitBack) + { + VIP_SendClientVIPMenu(iClient, false); + } + } + case MenuAction_Select: + { + char szOption[MESSAGE_LENGTH]; + hMenu.GetItem(iOpt2, szOption, sizeof(szOption)); + + int part = szOption[0] - 1; + if(part == BIND_MAX) + return; + + FeatureMenu(iClient, part).Display(iClient, MENU_TIME_FOREVER); + } + } +} + +Menu FeatureMenu(int iClient, const int msgPart) +{ + g_iBindNow[iClient] = BIND_MAX; + + Menu hMenu; + + char szBuffer[MESSAGE_LENGTH]; + VIP_GetClientVIPGroup(iClient, szBuffer, sizeof(szBuffer)); + + Json items = getItemsList(szBuffer, szBinds[msgPart]); + + if(!items) + return null; + + char szOption[MESSAGE_LENGTH], szValue[MESSAGE_LENGTH]; + JsonObject client = asJSONO(Packager.GetPackage(iClient).GetArtifact(pkgKey)); + + if(!(client.GetString(szBinds[msgPart], szValue, sizeof(szValue), true))) + delete client; + + hMenu = new Menu(FeatureMenu_CallBack); + hMenu.SetTitle("%T %T \n \n", "feature_title", iClient, szBinds[msgPart], iClient); + + for(int i, a, iDrawType; i < asJSONA(items).Size; i++) + { + szBuffer = NULL_STRING; + a = -1; + + asJSONA(items).GetString(i, szBuffer, sizeof(szBuffer)); + + if(StrContains(szBuffer, "disable", false) != -1) { + iDrawType = (szValue[0]) ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED; + a = 0; + } else if(StrContains(szBuffer, "custom", false) != -1) { + iDrawType = ITEMDRAW_DEFAULT; + a = 1; + } else { + iDrawType = (!strcmp(szBuffer, szValue)) ? ITEMDRAW_DISABLED : ITEMDRAW_DEFAULT; + } + + Format(szBuffer, sizeof(szBuffer), "%c%c%T", msgPart+1, i+1, szBuffer, iClient); + + ccp_replaceColors(szBuffer[2], true); + + if(a != -1) + Format(szBuffer, sizeof(szBuffer), "%s \n \n", szBuffer); + + hMenu.AddItem(szBuffer, szBuffer[2], iDrawType); + } + + hMenu.ExitButton = true; + hMenu.ExitBackButton = true; + + if(!hMenu.ItemCount) { + delete hMenu; + } + + delete items; + + return hMenu; +} + +public int FeatureMenu_CallBack(Menu hMenu, MenuAction action, int iClient, int iOpt2) +{ + switch(action) + { + case MenuAction_End: delete hMenu; + case MenuAction_Cancel: + { + if(iOpt2 == MenuCancel_ExitBack) + { + partsOfMsgsMenu(iClient).Display(iClient, MENU_TIME_FOREVER); + } + } + case MenuAction_Select: + { + char szOption[NAME_LENGTH]; + hMenu.GetItem(iOpt2, szOption, sizeof(szOption)); + + int part = szOption[0] - 1; + int idx = szOption[1] - 1; + + VIP_GetClientVIPGroup(iClient, szOption, sizeof(szOption)); + + JsonArray items = asJSONA(getItemsList(szOption, szBinds[part])); + + items.GetString(idx, szOption, sizeof(szOption)); + delete items; + + if(!strcmp(szOption, "custom")) { + g_iBindNow[iClient] = part; + + PrintToChat(iClient, "%T", "wait_custom_value", iClient); + return; + } + + Package pack = Packager.GetPackage(iClient); + + JsonObject artifact = asJSONO(pack.GetArtifact(pkgKey)); + + if(!strcmp(szOption, "disable")) { + artifact.Remove(szBinds[part]); + szOption = NULL_STRING; + } + else artifact.SetString(szBinds[part], szOption); + + if(pack.SetArtifact(pkgKey, artifact, freeAnyway)) { + + artifact = asJSONO(pack.GetArtifact(pkgKey)); + + char szBuffer[1024]; + if(!asJSON(artifact).Dump(szBuffer, sizeof(szBuffer), 0, true)) + delete artifact; + + cookie.Set(iClient,szBuffer); + } + + FeatureMenu(iClient, part).Display(iClient, MENU_TIME_FOREVER); + } + } +} + +public void OnClientPutInServer(int iClient) { + g_iBindNow[iClient] = BIND_MAX; +} + +public Action OnClientSayCommand(int iClient, const char[] command, const char[] args) +{ + if(!iClient || !IsClientInGame(iClient) || IsFakeClient(iClient) || IsChatTrigger()) + return Plugin_Continue; + + if(g_iBindNow[iClient] != BIND_MAX) + { + char szBuffer[MESSAGE_LENGTH]; + strcopy(szBuffer, sizeof(szBuffer), args); + + TrimString(szBuffer); + + if(g_iBindNow[iClient] != BIND_PREFIX && g_mPalette.FindString(szBuffer) == -1) + { + BreakPoint(g_iBindNow[iClient], szBuffer); + PrintToChat(iClient, "%T", "invalid_color_value", iClient, szBuffer); + + return Plugin_Handled; + } + + Package pack; + JsonObject model; + + if(!(pack = Packager.GetPackage(iClient)) || !(model = asJSONO(pack.GetArtifact(pkgKey)))) { + g_iBindNow[iClient] = BIND_MAX; + return Plugin_Handled; + } + + model.SetString(szBinds[g_iBindNow[iClient]], szBuffer); + + if(pack.SetArtifact(pkgKey, model, freeAnyway)) { + + model = asJSONO(pack.GetArtifact(pkgKey)); + + char szCookie[1024]; + if(!asJSON(model).Dump(szCookie, sizeof(szCookie), 0, true)) + delete model; + + cookie.Set(iClient, szCookie); + } + + PrintToChat(iClient, "%T", "ccp_custom_success", iClient); + + FeatureMenu(iClient, g_iBindNow[iClient]).Display(iClient, MENU_TIME_FOREVER); + + return Plugin_Handled; + } + + return Plugin_Continue; +} + +public Processing cc_proc_OnRebuildString(const int[] props, int part, ArrayList params, int &pLevel, char[] value, int size) { + if(!SENDER_INDEX(props[1]) || pLevel > level[part]) + return Proc_Continue; + + Package clientPack; + if(!(clientPack = Packager.GetPackage(SENDER_INDEX(props[1])))) + return Proc_Continue; + + Package serverPack; + JsonObject objModel; + if(!(serverPack = Packager.GetPackage(0)) || !(objModel = asJSONO(serverPack.GetArtifact(pkgKey)))) + return Proc_Continue; + + JsonArray channels; + + if(!(channels = asJSONA(objModel.Get("channels", true)))) { + delete objModel; + return Proc_Continue; + } + + char szIndent[64]; + params.GetString(0, szIndent, sizeof(szIndent)); + + if(!JsonArrayContainsString(channels, szIndent, true)) { + delete channels; + return Proc_Continue; + } + + delete channels; + + if(!(objModel = asJSONO(clientPack.GetArtifact(pkgKey)))) + return Proc_Continue; + + static char szValue[MESSAGE_LENGTH]; + if(!objModel.GetString(szBinds[part], szValue, sizeof(szValue), true) || !szValue[0]) { + delete objModel; + return Proc_Continue; + } + + if(part == BIND_PREFIX && TranslationPhraseExists(szValue)) + Format(szValue, sizeof(szValue), "%T", szValue, props[2]); + + pLevel = level[part]; + FormatEx(value, size, szValue); + + return Proc_Change; +} + +stock Json getItemsList(const char[] group, const char[] msgPart) { + Json first; + Json second; + + first = asJSON(Packager.GetPackage(0).GetArtifact(pkgKey)); + + if(!asJSONO(first).HasKey(group)) { + delete first; + return first; + } + + second = asJSON(asJSONO(first).Get(group)); + delete first; + + if(!asJSONO(second).HasKey(msgPart)) { + delete second; + return second; + } + + first = asJSON(asJSONO(second).Get(msgPart)); + delete second; + + return first; +} + +stock bool JsonArrayContainsString(const JsonArray array, const char[] str, bool casesens = true) { + + if(!array) + return false; + + char buffer[512]; + for(int i = 0; i < array.Size; i++) { + + if(!array.GetString(i, buffer, sizeof(buffer))) + continue; + + if(!strcmp(str, buffer, casesens)) + return true; + } + + return false; +} \ No newline at end of file diff --git a/ccp-vrtcp/scripting/ccp-vrtcp.sp b/ccp-vrtcp/scripting/ccp-vrtcp.sp new file mode 100644 index 0000000..07afd41 --- /dev/null +++ b/ccp-vrtcp/scripting/ccp-vrtcp.sp @@ -0,0 +1,62 @@ +#pragma newdecls required + +#include +#include + +public Plugin myinfo = +{ + name = "[CCP] RTCP ", + author = "nullent?", + description = "Realtime color processing", + version = "2.0.0", + url = "discord.gg/ChTyPUG" +}; + +bool bEnabled[MAXPLAYERS+1]; + +static const char g_szFeature[] = "advanced_message"; + +public void OnPluginStart() +{ + if(VIP_IsVIPLoaded()) + VIP_OnVIPLoaded(); +} + +public void VIP_OnVIPLoaded() +{ + VIP_RegisterFeature(g_szFeature, BOOL, TOGGLABLE, OnToogleFeature); +} + +public void OnMapStart() +{ + cc_proc_APIHandShake(cc_get_APIKey()); +} + +public void OnPluginEnd() +{ + if(!CanTestFeatures() + || GetFeatureStatus(FeatureType_Native, "VIP_UnregisterFeature") != FeatureStatus_Available) + return; + + VIP_UnregisterMe(); +} + +public void OnClientPutInServer(int iClient) +{ + bEnabled[iClient] = false; +} + +public void VIP_OnVIPClientLoaded(int iClient) +{ + bEnabled[iClient] = VIP_IsClientFeatureUse(iClient, g_szFeature) && VIP_GetClientFeatureStatus(iClient, g_szFeature) == ENABLED; +} + +public Action OnToogleFeature(int iClient, const char[] szFeature, VIP_ToggleState eOldStatus, VIP_ToggleState &eNewStatus) +{ + bEnabled[iClient] = eNewStatus == ENABLED; +} + +public bool cc_proc_SkipColors(const char[] indent, int sender) +{ + return bEnabled[sender]; +} \ No newline at end of file diff --git a/sourcemod/configs/ccprocessor/csgo.ini b/ccp/configs/ccprocessor/csgo.ini similarity index 100% rename from sourcemod/configs/ccprocessor/csgo.ini rename to ccp/configs/ccprocessor/csgo.ini diff --git a/sourcemod/configs/ccprocessor/cstrike.ini b/ccp/configs/ccprocessor/cstrike.ini similarity index 100% rename from sourcemod/configs/ccprocessor/cstrike.ini rename to ccp/configs/ccprocessor/cstrike.ini diff --git a/sourcemod/configs/ccprocessor/left4dead2.ini b/ccp/configs/ccprocessor/left4dead2.ini similarity index 100% rename from sourcemod/configs/ccprocessor/left4dead2.ini rename to ccp/configs/ccprocessor/left4dead2.ini diff --git a/sourcemod/configs/ccprocessor/tf.ini b/ccp/configs/ccprocessor/tf.ini similarity index 100% rename from sourcemod/configs/ccprocessor/tf.ini rename to ccp/configs/ccprocessor/tf.ini diff --git a/src/ccprocessor.sp b/ccp/scripting/ccprocessor.sp similarity index 100% rename from src/ccprocessor.sp rename to ccp/scripting/ccprocessor.sp diff --git a/src/ccprocessor/defaults.sp b/ccp/scripting/ccprocessor/defaults.sp similarity index 100% rename from src/ccprocessor/defaults.sp rename to ccp/scripting/ccprocessor/defaults.sp diff --git a/src/ccprocessor/forwards.sp b/ccp/scripting/ccprocessor/forwards.sp similarity index 100% rename from src/ccprocessor/forwards.sp rename to ccp/scripting/ccprocessor/forwards.sp diff --git a/src/ccprocessor/natives.sp b/ccp/scripting/ccprocessor/natives.sp similarity index 100% rename from src/ccprocessor/natives.sp rename to ccp/scripting/ccprocessor/natives.sp diff --git a/sourcemod/translations/ccp_core.phrases.txt b/ccp/translations/ccp_core.phrases.txt similarity index 100% rename from sourcemod/translations/ccp_core.phrases.txt rename to ccp/translations/ccp_core.phrases.txt diff --git a/sourcemod/translations/ccp_engine.phrases.txt b/ccp/translations/ccp_engine.phrases.txt similarity index 100% rename from sourcemod/translations/ccp_engine.phrases.txt rename to ccp/translations/ccp_engine.phrases.txt diff --git a/include/UTF-8-string.inc b/include/UTF-8-string.inc new file mode 100644 index 0000000..b72ee1e --- /dev/null +++ b/include/UTF-8-string.inc @@ -0,0 +1,213 @@ +/************************************************************************** + * UTF-8 string * + * Автор: Феникс(komashchenko) * + * Version: 1.1 * + * http://zizt.ru/ http://hlmod.ru/ * + * 03.05.2015 - V1.0: Релиз * + * 02.06.2015 - V1.1: Исправлены баги * + **************************************************************************/ + + +#include + +/** + * Returns whether two strings are equal. + * + * @param str1 First string (left). + * @param str2 Second string (right). + * @param caseSensitive If true (default), comparison is case sensitive. + * If false, comparison is case insensitive. + * @return True if equal, false otherwise. + */ +stock bool:UTF8StrEqual(const String:str1[], const String:str2[], bool:caseSensitive=true) +{ + return (UTF8strcmp(str1, str2, caseSensitive) == 0); +} + +/** + * Compares two strings parts lexographically. + * + * @param str1 First string (left). + * @param str2 Second string (right). + * @param num Number of characters to compare. + * @param caseSensitive If true (default), comparison is case sensitive. + * If false, comparison is case insensitive. + * @return -1 if str1 < str2 + * 0 if str1 == str2 + * 1 if str1 > str2 + */ +stock UTF8strncmp(const String:str1[], const String:str2[], num, bool:caseSensitive=true) +{ + if(!caseSensitive) + { + new a = strlen(str1)+190, b = strlen(str2)+190; + decl String:aa[a], String:bb[b]; + strcopy(aa, a, str1); + strcopy(bb, b, str2); + UTF8CharToLower(aa); + UTF8CharToLower(bb); + return strncmp(aa, bb, num, true); + } + return strncmp(str1, str2, num, true); +} + +/** + * Compares two strings lexographically. + * + * @param str1 First string (left). + * @param str2 Second string (right). + * @param caseSensitive If true (default), comparison is case sensitive. + * If false, comparison is case insensitive. + * @return -1 if str1 < str2 + * 0 if str1 == str2 + * 1 if str1 > str2 + */ +stock UTF8strcmp(const String:str1[], const String:str2[], bool:caseSensitive=true) +{ + if(!caseSensitive) + { + new a = strlen(str1)+190, b = strlen(str2)+190; + decl String:aa[a], String:bb[b]; + strcopy(aa, a, str1); + strcopy(bb, b, str2); + UTF8CharToLower(aa); + UTF8CharToLower(bb); + return strcmp(aa, bb, true); + } + return strcmp(str1, str2, true); +} + +/** + * Tests whether a string is found inside another string. + * + * @param str String to search in. + * @param substr Substring to find inside the original string. + * @param caseSensitive If true (default), search is case sensitive. + * If false, search is case insensitive. + * @return -1 on failure (no match found). Any other value + * indicates a position in the string where the match starts. + */ +stock UTF8StrContains(const String:str[], const String:substr[], bool:caseSensitive=true) +{ + if(!caseSensitive) + { + new a = strlen(str)+190, b = strlen(substr)+190; + decl String:aa[a], String:bb[b]; + strcopy(aa, a, str); + strcopy(bb, b, substr); + UTF8CharToLower(aa); + UTF8CharToLower(bb); + return StrContains(aa, bb, true); + } + return StrContains(str, substr, true); +} + +/** + * Делает с нижнего регистра верхний. + * + * @param string строка для конвертации. + * @return количество символов которые были переведены в верхний регистр + */ +stock UTF8CharToUpper(String:string[]) +{ + new I = 0, O = strlen(string), Byte, K; + while(I < O) + { + Byte = string[I]; + if(Byte >= 128) + { + if(Byte >= 240) + { + if(strlen(string[I]) < 4) break; + else I += 4; + } + else if(Byte >= 224) + { + if(strlen(string[I]) < 3) break; + else I += 3; + } + else if(Byte >= 192) + { + if(strlen(string[I]) < 2) break; + Byte = (Byte % 32) * 64; + Byte += (string[I+1] % 64); + if(1072 <= Byte <= 1103 || Byte == 1105 || 1110 <= Byte <= 1111 || Byte == 1169) + { + if(1105 <= Byte <= 1111) Byte = Byte-80; + else if(Byte != 1169) Byte = Byte-32; + else Byte--; + string[I] = 192 + RoundToFloor(float(Byte / 64)); + string[I+1] = 128 + (Byte % 64); + K++; + } + I += 2; + } + else break; + } + else + { + if(97 <= Byte <= 122) + { + string[I] = Byte-32; + K++; + } + I += 1; + } + } + return K; +} + +/** + * Делает с верхнего регистра нижний. + * + * @param string строка для конвертации. + * @return количество символов которые были переведены в нижний регистр + */ +stock UTF8CharToLower(String:string[]) +{ + new I = 0, O = strlen(string), Byte, K; + while(I < O) + { + Byte = string[I]; + if(Byte >= 128) + { + if(Byte >= 240) + { + if(strlen(string[I]) < 4) break; + else I += 4; + } + else if(Byte >= 224) + { + if(strlen(string[I]) < 3) break; + else I += 3; + } + else if(Byte >= 192) + { + if(strlen(string[I]) < 2) break; + Byte = (Byte % 32) * 64; + Byte += (string[I+1] % 64); + if(1040 <= Byte <= 1071 || Byte == 1025 || 1030 <= Byte <= 1031 || Byte == 1168) + { + if(1025 <= Byte <= 1031) Byte = Byte+80; + else if(Byte != 1168) Byte = Byte+32; + else Byte++; + string[I] = 192 + RoundToFloor(float(Byte / 64)); + string[I+1] = 128 + (Byte % 64); + K++; + } + I += 2; + } + else break; + } + else + { + if(65 <= Byte <= 90) + { + string[I] = Byte+32; + K++; + } + I++; + } + } + return K; +} diff --git a/src/include/ccprocessor.inc b/include/ccprocessor.inc similarity index 100% rename from src/include/ccprocessor.inc rename to include/ccprocessor.inc diff --git a/src/include/ccprocessor/debug.inc b/include/ccprocessor/debug.inc similarity index 100% rename from src/include/ccprocessor/debug.inc rename to include/ccprocessor/debug.inc diff --git a/src/include/ccprocessor/forwards.inc b/include/ccprocessor/forwards.inc similarity index 100% rename from src/include/ccprocessor/forwards.inc rename to include/ccprocessor/forwards.inc diff --git a/src/include/ccprocessor/modules/channels/ccp-channels.inc b/include/ccprocessor/modules/channels/ccp-channels.inc similarity index 100% rename from src/include/ccprocessor/modules/channels/ccp-channels.inc rename to include/ccprocessor/modules/channels/ccp-channels.inc diff --git a/src/include/ccprocessor/natives.inc b/include/ccprocessor/natives.inc similarity index 100% rename from src/include/ccprocessor/natives.inc rename to include/ccprocessor/natives.inc diff --git a/src/include/ccprocessor/stocks.inc b/include/ccprocessor/stocks.inc similarity index 100% rename from src/include/ccprocessor/stocks.inc rename to include/ccprocessor/stocks.inc diff --git a/third-party/packager b/third-party/packager new file mode 160000 index 0000000..67ec448 --- /dev/null +++ b/third-party/packager @@ -0,0 +1 @@ +Subproject commit 67ec448cdde403214ab9be87aff80d865ff3786d diff --git a/third-party/shop b/third-party/shop new file mode 160000 index 0000000..b0db298 --- /dev/null +++ b/third-party/shop @@ -0,0 +1 @@ +Subproject commit b0db298106a4c988090700548151c3581d06b46d diff --git a/third-party/sm-jansson b/third-party/sm-jansson new file mode 160000 index 0000000..a77535a --- /dev/null +++ b/third-party/sm-jansson @@ -0,0 +1 @@ +Subproject commit a77535ae78904305c7f6a5afa5e1216b8efdec0e diff --git a/third-party/storage b/third-party/storage new file mode 160000 index 0000000..39a2089 --- /dev/null +++ b/third-party/storage @@ -0,0 +1 @@ +Subproject commit 39a208972496bf411ff2fc3eb8162974c14f790a diff --git a/third-party/vip b/third-party/vip new file mode 160000 index 0000000..63f4a33 --- /dev/null +++ b/third-party/vip @@ -0,0 +1 @@ +Subproject commit 63f4a332e7b539fac1a287450752ea7f4349c136