├── .dockerignore ├── .github ├── FUNDING.yml └── workflows │ └── build.yml ├── .gitattributes ├── src ├── tests │ ├── client.c │ ├── memory.c │ ├── config.c │ ├── strings.c │ └── world.c ├── platform.c ├── types │ ├── groups.h │ ├── list.h │ ├── netbuffer.h │ ├── compr.h │ ├── assoc.h │ ├── plugin.h │ ├── command.h │ ├── websock.h │ ├── world.h │ ├── platform.h │ ├── config.h │ ├── keys.h │ ├── event.h │ ├── client.h │ ├── protocol.h │ └── block.h ├── groups.h ├── strstor.h ├── tests.h ├── csmath.h ├── consoleio.h ├── tests.c ├── timer.h ├── cserror.h ├── pager.h ├── generators │ └── flat.c ├── event.h ├── netbuffer.h ├── list.h ├── log.h ├── groups.c ├── server.h ├── cpe.h ├── csmath.c ├── main.c ├── timer.c ├── generators.h ├── websock.h ├── list.c ├── config.h ├── hash.h ├── generators.c ├── str.h ├── vector.h ├── http.h ├── cserror.c ├── event.c ├── assoc.h ├── world.h ├── block.h ├── compr.h ├── heartbeat.h ├── assoc.c ├── command.h ├── platform.h ├── hash.c ├── netbuffer.c ├── consoleio.c ├── block.c ├── platforms │ └── shared.c ├── strstor.c ├── http.c ├── client.h ├── protocol.h ├── str.c ├── compr.c ├── plugin.h ├── log.c └── cpe.c ├── .gitignore ├── Dockerfile ├── .vscode ├── tasks.json └── launch.json ├── misc ├── gitthing.bat ├── vsdetect.bat └── zdetect.bat ├── LICENSE ├── version.rc └── README.md /.dockerignore: -------------------------------------------------------------------------------- 1 | **/out 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: [ "https://funding.igvx.ru/" ] 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.c text eol=lf 2 | *.h text eol=lf 3 | *.bat text eol=crlf 4 | -------------------------------------------------------------------------------- /src/tests/client.c: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | #include "client.h" 3 | #include "tests.h" 4 | 5 | cs_bool Tests_Client(void) { 6 | 7 | return true; 8 | } 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/c_cpp_properties.json 2 | .vscode/settings.json 3 | zlib.path 4 | out/ 5 | vars.sh 6 | vars.bat 7 | 8 | desktop.ini 9 | .DS_Store 10 | .DS_Store? 11 | ._* 12 | .Spotlight-V100 13 | .Trashes 14 | -------------------------------------------------------------------------------- /src/platform.c: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | #include "platform.h" 3 | #include "platforms/shared.c" 4 | 5 | #if defined(CORE_USE_WINDOWS) 6 | # include "platforms/windows.c" 7 | #elif defined(CORE_USE_UNIX) 8 | # include "platforms/unix.c" 9 | #endif 10 | -------------------------------------------------------------------------------- /src/types/groups.h: -------------------------------------------------------------------------------- 1 | #ifndef GROUPSTYPES_H 2 | #define GROUPSTYPES_H 3 | #include "core.h" 4 | 5 | #define GROUPS_INVALID_ID (cs_uintptr)-1 6 | 7 | typedef struct _CGroup { 8 | cs_byte rank; 9 | cs_char name[MAX_STR_LEN]; 10 | } CGroup; 11 | #endif 12 | -------------------------------------------------------------------------------- /src/groups.h: -------------------------------------------------------------------------------- 1 | #ifndef GROUPS_H 2 | #define GROUPS_H 3 | #include "core.h" 4 | #include "types/groups.h" 5 | 6 | API cs_uintptr Groups_Create(cs_str name, cs_byte rank); 7 | API CGroup *Groups_GetByID(cs_uintptr gid); 8 | API cs_bool Groups_Remove(cs_uintptr gid); 9 | #endif 10 | -------------------------------------------------------------------------------- /src/strstor.h: -------------------------------------------------------------------------------- 1 | #ifndef STRSTOR_H 2 | #define STRSTOR_H 3 | #include "core.h" 4 | 5 | #ifndef CORE_BUILD_PLUGIN 6 | cs_bool Sstor_Defaults(void); 7 | #endif 8 | 9 | API cs_bool Sstor_IsExists(cs_str key); 10 | API cs_str Sstor_Get(cs_str key); 11 | API cs_bool Sstor_Set(cs_str key, cs_str value); 12 | API void Sstor_Cleanup(void); 13 | #endif 14 | -------------------------------------------------------------------------------- /src/tests.h: -------------------------------------------------------------------------------- 1 | #ifdef CORE_BUILD_PLUGIN 2 | # error Tests cannot be used in plugins 3 | #endif 4 | 5 | #ifndef TESTS_H 6 | #define TESTS_H 7 | #include "core.h" 8 | #include "log.h" 9 | 10 | extern cs_uint16 Tests_CurrNum; 11 | extern cs_str Tests_Current; 12 | void Tests_NewTask(cs_str name); 13 | #define Tests_Assert(expr, desc) if(!(expr)) { \ 14 | Log_Error("\tFailed: %s.", desc); \ 15 | return false; \ 16 | } 17 | cs_bool Tests_PerformAll(void); 18 | #endif 19 | -------------------------------------------------------------------------------- /src/csmath.h: -------------------------------------------------------------------------------- 1 | #ifndef CSMATH_H 2 | #define CSMATH_H 3 | #include "core.h" 4 | 5 | #define Math_Sq(N) ((N) * (N)) 6 | 7 | typedef cs_uint64 RNGState; 8 | API void Random_Seed(RNGState *rnd, cs_int32 seed); 9 | API void Random_SeedFromTime(RNGState *rnd); 10 | API cs_int32 Random_Next(RNGState *rnd, cs_int32 n); 11 | API cs_float Random_Float(RNGState *rnd); 12 | API cs_int32 Random_Range(RNGState *rnd, cs_int32 min, cs_int32 max); 13 | API cs_float Math_Sqrt(cs_float f); 14 | #endif 15 | -------------------------------------------------------------------------------- /src/types/list.h: -------------------------------------------------------------------------------- 1 | #ifndef LISTTYPES_H 2 | #define LISTTYPES_H 3 | #include "core.h" 4 | 5 | typedef union _MultiValue { 6 | cs_byte num8; 7 | cs_uint16 num16; 8 | cs_uint32 num32; 9 | cs_uintptr numptr; 10 | cs_str str; 11 | void *ptr; 12 | } UMultiValue; 13 | 14 | typedef struct _AListField { 15 | UMultiValue value; 16 | struct _AListField *next, *prev; 17 | } AListField; 18 | 19 | typedef struct _KListField { 20 | UMultiValue key, value; 21 | struct _KListField *next, *prev; 22 | } KListField; 23 | #endif 24 | -------------------------------------------------------------------------------- /src/consoleio.h: -------------------------------------------------------------------------------- 1 | #ifdef CORE_BUILD_PLUGIN 2 | # error ConsoleIO cannot be used in plugins 3 | #endif 4 | 5 | #ifndef CONSOLEIO_H 6 | #define CONSOLEIO_H 7 | #include "core.h" 8 | 9 | #if defined(CORE_USE_WINDOWS) 10 | # define CONSOLEIO_TERMINATE CTRL_C_EVENT 11 | #elif defined(CORE_USE_UNIX) 12 | # include 13 | # define CONSOLEIO_TERMINATE SIGINT 14 | #endif 15 | 16 | void ConsoleIO_PrePrint(void); 17 | void ConsoleIO_AfterPrint(void); 18 | cs_bool ConsoleIO_Init(void); 19 | void ConsoleIO_Uninit(void); 20 | #endif 21 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | FROM ubuntu:latest 3 | WORKDIR /cserver 4 | ENTRYPOINT ./out/$(gcc -dumpmachine)-dbg/server 5 | 6 | COPY . . 7 | 8 | RUN apt-get update && apt-get install -y \ 9 | gcc make zlib1g-dev git-core libluajit-5.1-dev pkg-config dpkg-dev 10 | 11 | RUN git pull; exit 0 12 | RUN git clone https://github.com/igor725/cs-base ../cs-base 13 | RUN git clone https://github.com/igor725/cs-lua ../cs-lua 14 | 15 | RUN ./build wall dbg 16 | RUN ./build wall dbg pb base install 17 | RUN ./build wall dbg pb lua install pkg 18 | 19 | EXPOSE 25565/tcp 20 | -------------------------------------------------------------------------------- /src/tests.c: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | #include "log.h" 3 | #include "tests.h" 4 | 5 | #include "tests/memory.c" 6 | #include "tests/strings.c" 7 | #include "tests/client.c" 8 | #include "tests/world.c" 9 | #include "tests/config.c" 10 | 11 | cs_uint16 Tests_CurrNum = 0; 12 | cs_str Tests_Current = NULL; 13 | void Tests_NewTask(cs_str name) { 14 | Log_Info("Test %d: %s", ++Tests_CurrNum, name); 15 | Tests_Current = name; 16 | } 17 | 18 | cs_bool Tests_PerformAll(void) { 19 | return Tests_Memory() && 20 | Tests_Strings() && 21 | Tests_Client() && 22 | Tests_World() && 23 | Tests_Config(); 24 | } 25 | -------------------------------------------------------------------------------- /src/types/netbuffer.h: -------------------------------------------------------------------------------- 1 | #ifndef NETBUFFERTYPES_H 2 | #define NETBUFFERTYPES_H 3 | #include "core.h" 4 | #include "types/platform.h" 5 | 6 | #define GROWINGBUFFER_ADDITIONAL 512 7 | 8 | typedef struct _GrowingBuffer { 9 | cs_uint32 offset, size; 10 | cs_uint32 maxsize; 11 | cs_char *buffer; 12 | } GrowingBuffer; 13 | 14 | typedef struct _NetBuffer { 15 | Socket fd; 16 | cs_uint32 cread, cwrite; 17 | GrowingBuffer write; 18 | GrowingBuffer read; 19 | cs_bool closed; 20 | cs_bool shutdown; 21 | cs_bool asframe; 22 | cs_bool wsupgrade; 23 | cs_uint32 framesize; 24 | } NetBuffer; 25 | #endif 26 | -------------------------------------------------------------------------------- /src/types/compr.h: -------------------------------------------------------------------------------- 1 | #ifndef COMPRTYPES_H 2 | #define COMPRTYPES_H 3 | #include "core.h" 4 | 5 | typedef enum _ComprType { 6 | COMPR_TYPE_NOTSET, 7 | COMPR_TYPE_DEFLATE, 8 | COMPR_TYPE_INFLATE, 9 | COMPR_TYPE_UNGZIP, 10 | COMPR_TYPE_GZIP 11 | } ComprType; 12 | 13 | typedef enum _ComprState { 14 | COMPR_STATE_IDLE, 15 | COMPR_STATE_INPROCESS, 16 | COMPR_STATE_FINISHING, 17 | COMPR_STATE_DONE 18 | } ComprState; 19 | 20 | typedef struct _Compr { 21 | ComprState state; 22 | ComprType type; 23 | cs_int32 ret; 24 | cs_uint32 written; 25 | cs_uint32 queued; 26 | void *stream; 27 | } Compr; 28 | #endif 29 | -------------------------------------------------------------------------------- /src/timer.h: -------------------------------------------------------------------------------- 1 | #ifndef TIMER_H 2 | #define TIMER_H 3 | #include "core.h" 4 | 5 | typedef void(*TimerCallback)(cs_int32, cs_int32, void *); 6 | #define TIMER_FUNC(N) \ 7 | static void N(cs_int32 ticks, cs_int32 left, void *ud) 8 | 9 | typedef struct _Timer { 10 | cs_int32 delay, nexttick, ticks, left; 11 | TimerCallback callback; 12 | void *userdata; 13 | } Timer; 14 | 15 | #ifndef CORE_BUILD_PLUGIN 16 | void Timer_RemoveAll(void); 17 | void Timer_Update(cs_int32 delta); 18 | #endif 19 | 20 | API Timer *Timer_Add(cs_int32 ticks, cs_uint32 delay, TimerCallback callback, void *ud); 21 | API void Timer_Remove(Timer *timer); 22 | #endif 23 | -------------------------------------------------------------------------------- /src/types/assoc.h: -------------------------------------------------------------------------------- 1 | #ifndef ASSOCTYPES_H 2 | #define ASSOCTYPES_H 3 | #include "core.h" 4 | 5 | #define ASSOC_INVALID_TYPE ((AssocType)-1) 6 | 7 | /** 8 | * @brief Перечисление возможных объектов для создания ассоцииации 9 | * 10 | */ 11 | typedef enum _EAssocBindType { 12 | ASSOC_BIND_WORLD, /** Тип используется для ассоциации с мирами */ 13 | ASSOC_BIND_CLIENT /** Тип используется для ассоциации с игроками */ 14 | } EAssocBindType; 15 | 16 | /** 17 | * @brief Уникальный номер ассоциативного типа 18 | * 19 | */ 20 | typedef cs_int16 AssocType; 21 | 22 | /** 23 | * @brief Ассоциированная память 24 | * 25 | */ 26 | typedef void *AssocMem; 27 | #endif 28 | -------------------------------------------------------------------------------- /src/cserror.h: -------------------------------------------------------------------------------- 1 | #ifndef ERROR_H 2 | #define ERROR_H 3 | #include "core.h" 4 | 5 | #if defined(CORE_USE_WINDOWS) 6 | # define Error_GetSysCode() (cs_error)GetLastError() 7 | #elif defined(CORE_USE_UNIX) 8 | # define Error_GetSysCode() (cs_error)errno 9 | #endif 10 | 11 | #define _Error_Print(ecode, abort) Error_Print(abort, ecode, __FILE__, __LINE__, __func__) 12 | #define Error_PrintSys(abort) _Error_Print(Error_GetSysCode(), abort) 13 | 14 | #ifndef CORE_BUILD_PLUGIN 15 | cs_bool Error_Init(void); 16 | void Error_Uninit(void); 17 | #endif 18 | 19 | API void Error_Print(cs_bool abort, cs_int32 code, cs_str file, cs_uint32 line, cs_str func, ...); 20 | #endif // ERROR_H 21 | -------------------------------------------------------------------------------- /src/pager.h: -------------------------------------------------------------------------------- 1 | #ifndef PAGER_H 2 | #define PAGER_H 3 | #include "core.h" 4 | 5 | typedef struct _Pager { 6 | cs_int32 state; 7 | cs_int32 plen; 8 | cs_int32 pcur; 9 | } Pager; 10 | 11 | #define PAGER_DEFAULT_PAGELEN 10 12 | 13 | #define Pager_Init(c, p) (Pager){.state = (c < 1 ? 0 : (c - 1)) * (p), .plen = p, .pcur = c < 1 ? 1 : c} 14 | #define Pager_Step(s) if((s).state-- > 0) continue; else if((s).state < -(s).plen) continue; 15 | #define Pager_IsDirty(s) (((s).state < -(s).plen) || ((s).pcur != 1)) 16 | #define Pager_CurrentPage(s) ((s).pcur) 17 | #define Pager_EntriesLeft(s) (((s).plen * ((s).pcur - 1)) - (s).state) 18 | #define Pager_CountPages(s) (cs_int32)(Pager_EntriesLeft(s) / (cs_float)((s).plen) + 0.999f) 19 | #endif 20 | -------------------------------------------------------------------------------- /src/generators/flat.c: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | #include "block.h" 3 | #include "world.h" 4 | 5 | cs_bool flatgenerator(World *world, cs_uint32 seed) { 6 | (void)seed; 7 | WorldInfo *wi = &world->info; 8 | SVec *dims = &wi->dimensions; 9 | 10 | BlockID *blocks = World_GetBlockArray(world, NULL); 11 | cs_uint32 dirtEnd = dims->x * dims->z * (dims->y / 2 - 1); 12 | for(cs_uint32 i = 0; i < dirtEnd + dims->x * dims->z; i++) 13 | blocks[i] = i < dirtEnd ? BLOCK_DIRT : BLOCK_GRASS; 14 | 15 | World_SetEnvProp(world, WORLD_PROP_CLOUDSLEVEL, dims->y + 2); 16 | World_SetEnvProp(world, WORLD_PROP_EDGELEVEL, dims->y / 2); 17 | 18 | wi->spawnVec.x = (float)dims->x / 2; 19 | wi->spawnVec.y = (float)(dims->y / 2) + 1.59375f; 20 | wi->spawnVec.z = (float)dims->z / 2; 21 | return true; 22 | } 23 | -------------------------------------------------------------------------------- /src/event.h: -------------------------------------------------------------------------------- 1 | #ifndef EVENT_H 2 | #define EVENT_H 3 | #include "core.h" 4 | #include "types/event.h" 5 | 6 | #define Event_DeclareBunch(N) static EventRegBunch N[] = 7 | #define Event_DeclarePubBunch(N) EventRegBunch N[] = 8 | #define EVENT_BUNCH_ADD(R, E, F) {R, E, (void*)F} 9 | #define EVENT_BUNCH_END {0, 0, NULL} 10 | 11 | API cs_bool Event_RegisterVoid(EventType type, evtVoidCallback func); 12 | API cs_bool Event_RegisterBool(EventType type, evtBoolCallback func); 13 | API cs_bool Event_RegisterBunch(EventRegBunch *bunch); 14 | API cs_bool Event_Unregister(EventType type, void *evtFuncPtr); 15 | API void Event_UnregisterBunch(EventRegBunch *bunch); 16 | API cs_bool Event_Call(EventType type, void *param); 17 | 18 | #ifndef CORE_BUILD_PLUGIN 19 | NOINL void Event_UnregisterAll(void); 20 | #endif 21 | 22 | #endif // EVENT_H 23 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Build debug", 6 | "type": "shell", 7 | "windows" : { 8 | "command": "build.bat" 9 | }, 10 | "linux": { 11 | "command": "./build" 12 | }, 13 | "args": ["wall", "dbg", "noprompt"], 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true 17 | }, 18 | "presentation": { 19 | "reveal": "silent" 20 | }, 21 | "problemMatcher": "$msCompile" 22 | }, 23 | { 24 | "label": "Build release", 25 | "type": "shell", 26 | "windows" : { 27 | "command": "build.bat" 28 | }, 29 | "linux": { 30 | "command": "./build" 31 | }, 32 | "args": ["wall", "noprompt"], 33 | "group": "build", 34 | "presentation": { 35 | "reveal": "silent" 36 | }, 37 | "problemMatcher": "$msCompile" 38 | } 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /src/netbuffer.h: -------------------------------------------------------------------------------- 1 | #ifndef NETBUFFER_H 2 | #define NETBUFFER_H 3 | #include "core.h" 4 | #include "types/netbuffer.h" 5 | 6 | API void NetBuffer_Init(NetBuffer *nb, Socket sock); 7 | API cs_bool NetBuffer_Process(NetBuffer *nb); 8 | API cs_char *NetBuffer_PeekRead(NetBuffer *nb, cs_uint32 point); 9 | API cs_int32 NetBuffer_ReadLine(NetBuffer *nb, cs_char *buffer, cs_uint32 buflen); 10 | API cs_char *NetBuffer_Read(NetBuffer *nb, cs_uint32 len); 11 | API cs_char *NetBuffer_StartWrite(NetBuffer *nb, cs_uint32 dlen); 12 | API cs_bool NetBuffer_EndWrite(NetBuffer *nb, cs_uint32 dlen); 13 | API cs_uint32 NetBuffer_AvailRead(NetBuffer *nb); 14 | API cs_uint32 NetBuffer_AvailWrite(NetBuffer *nb); 15 | API cs_bool NetBuffer_Shutdown(NetBuffer *nb); 16 | API cs_bool NetBuffer_IsValid(NetBuffer *nb); 17 | API cs_bool NetBuffer_IsAlive(NetBuffer *nb); 18 | API void NetBuffer_ForceClose(NetBuffer *nb); 19 | #endif 20 | -------------------------------------------------------------------------------- /misc/gitthing.bat: -------------------------------------------------------------------------------- 1 | @REM Сделать тут умное обновление 2 | git --version >nul 3 | IF !ERRORLEVEL! EQU 0 SET GITOK=1 4 | 5 | IF !GITOK! EQU 1 ( 6 | IF !GITUPD! EQU 1 ( 7 | git -C !ROOT! fetch && git -C !ROOT! merge --ff-only 8 | IF NOT "!ERRORLEVEL!"=="0" GOTO :gitfail 9 | ) 10 | 11 | SET TAG_INSTALLED=0 12 | 13 | FOR /F "tokens=* USEBACKQ" %%F IN (`git -C "!ROOT!" describe --tags HEAD 2^> nul`) DO ( 14 | SET CFLAGS=!CFLAGS! /DGIT_COMMIT_TAG#\"%%F\" 15 | SET TAG_INSTALLED=1 16 | ) 17 | 18 | IF !TAG_INSTALLED! EQU 0 ( 19 | FOR /F "tokens=* USEBACKQ" %%F IN (`git -C "!ROOT!" rev-parse --short HEAD 2^> nul`) DO 2> nul ( 20 | SET CFLAGS=!CFLAGS! /DGIT_COMMIT_TAG#\"%%F\" 21 | ) 22 | ) 23 | 24 | EXIT /B 0 25 | ) 26 | 27 | ECHO GITTHING: GIT binary was not found 28 | EXIT /B 0 29 | 30 | :gitfail 31 | ECHO GITTHING: Failed to fetch repository changes 32 | EXIT /B 1 33 | -------------------------------------------------------------------------------- /src/list.h: -------------------------------------------------------------------------------- 1 | #ifndef LIST_H 2 | #define LIST_H 3 | #include "core.h" 4 | #include "types/list.h" 5 | 6 | #define List_Next(field) (field)->prev 7 | 8 | #define List_Iter(field, head) \ 9 | for(field = head; field != NULL; field = List_Next(field)) 10 | 11 | API AListField *AList_AddField(AListField **head, void *value); 12 | API cs_bool AList_Iter(AListField **head, void *ud, cs_bool(*callback)(AListField *, AListField **, void *)); 13 | API UMultiValue AList_GetValue(AListField *field); 14 | API void AList_Remove(AListField **head, AListField *field); 15 | 16 | API KListField *KList_AddField(KListField **head, void *key, void *value); 17 | API cs_bool KList_Iter(KListField **head, void *ud, cs_bool(*callback)(KListField *, KListField **, void *)); 18 | API UMultiValue KList_GetKey(KListField *field); 19 | API UMultiValue KList_GetValue(KListField *field); 20 | API void KList_Remove(KListField **head, KListField *field); 21 | #endif 22 | -------------------------------------------------------------------------------- /src/types/plugin.h: -------------------------------------------------------------------------------- 1 | #ifndef PLUGINTYPES_H 2 | #define PLUGINTYPES_H 3 | #include "core.h" 4 | #include "types/platform.h" 5 | #include "types/list.h" 6 | 7 | typedef struct _PluginInterface { 8 | cs_str iname; 9 | void *iptr; 10 | cs_size isize; 11 | } PluginInterface; 12 | 13 | typedef struct _PluginInfo { 14 | cs_uint32 id, version; 15 | cs_str name, home; 16 | } PluginInfo; 17 | 18 | typedef cs_bool(*pluginInitFunc)(void); 19 | typedef cs_bool(*pluginInitExFunc)(cs_uint32 id); 20 | typedef cs_bool(*pluginUnloadFunc)(cs_bool); 21 | typedef void(*pluginReceiveIface)(cs_str name, void *ptr, cs_size size); 22 | typedef cs_str(*pluginUrlFunc)(void); 23 | 24 | typedef struct _Plugin { 25 | cs_uint32 id, version; 26 | cs_str name; 27 | void *lib; 28 | PluginInterface *ifaces; 29 | pluginReceiveIface irecv; 30 | pluginUnloadFunc unload; 31 | pluginUrlFunc url; 32 | AListField *ireqHead; 33 | AListField *ireqHold; 34 | Mutex *mutex; 35 | } Plugin; 36 | #endif 37 | -------------------------------------------------------------------------------- /src/log.h: -------------------------------------------------------------------------------- 1 | #ifndef LOG_H 2 | #define LOG_H 3 | #include "core.h" 4 | #include 5 | 6 | #define LOG_QUIET 0x00 7 | #define LOG_ERROR BIT(0) 8 | #define LOG_INFO BIT(1) 9 | #define LOG_CHAT BIT(2) 10 | #define LOG_WARN BIT(3) 11 | #define LOG_DEBUG BIT(4) 12 | #define LOG_COLORS BIT(5) 13 | #define LOG_REPEAT BIT(6) // Пока что не работает корректно 14 | #define LOG_ALL 0x0F 15 | #define LOG_BUFSIZE 8192 16 | 17 | typedef struct _LogBuffer { 18 | cs_char data[LOG_BUFSIZE]; 19 | cs_size offset; 20 | cs_byte flag; 21 | } LogBuffer; 22 | 23 | #ifndef CORE_BUILD_PLUGIN 24 | cs_bool Log_Init(void); 25 | void Log_Uninit(void); 26 | void Log_Print(cs_byte flag, cs_str str, va_list *args); 27 | #endif 28 | 29 | API void Log_Gen(cs_byte flag, cs_str str, ...); 30 | API void Log_Error(cs_str str, ...); 31 | API void Log_Info(cs_str str, ...); 32 | API void Log_Chat(cs_str str, ...); 33 | API void Log_Warn(cs_str str, ...); 34 | API void Log_Debug(cs_str str, ...); 35 | 36 | API void Log_SetLevelStr(cs_str str); 37 | VAR cs_byte Log_Flags; 38 | #endif // LOG_H 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 igor725 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /misc/vsdetect.bat: -------------------------------------------------------------------------------- 1 | WHERE /Q CL.EXE 2 | IF !ERRORLEVEL! NEQ 0 ( 3 | IF "!PROCESSOR_ARCHITECTURE!"=="x86" ( 4 | SET VCFILE=vcvars32 5 | CALL :testforvc "!ProgramFiles!\Microsoft Visual Studio" 6 | IF !ERRORLEVEL! NEQ 0 GOTO :vcerror 7 | ) ELSE IF "!PROCESSOR_ARCHITECTURE!"=="AMD64" ( 8 | SET VCFILE=vcvars64 9 | CALL :testforvc "!ProgramFiles(x86)!\Microsoft Visual Studio" 10 | IF !ERRORLEVEL! NEQ 0 CALL :testforvc "!ProgramFiles!\Microsoft Visual Studio" 11 | IF !ERRORLEVEL! NEQ 0 GOTO :vcerror 12 | ) ELSE ( 13 | ECHO Unknown architecture 14 | EXIT /B 1 15 | ) 16 | ) 17 | 18 | :vcok 19 | SET ARCH=!VSCMD_ARG_TGT_ARCH! 20 | IF "!ARCH!"=="" GOTO vcerror 21 | EXIT /B 0 22 | 23 | :testforvc 24 | FOR /F "tokens=* USEBACKQ" %%F IN (`DIR /B /AD %* 2^> nul`) DO ( 25 | SET VCVARSALL=%1\%%F\BuildTools\VC\Auxiliary\Build\!VCFILE!.bat 26 | IF EXIST !VCVARSALL! ( 27 | CALL !VCVARSALL! 28 | IF !ERRORLEVEL! NEQ 0 GOTO :vcerror 29 | GOTO :vcok 30 | ) 31 | ) 32 | EXIT /B 0 33 | 34 | :vcerror 35 | ECHO You don't have Visual Studio C++ BuildTools installed, you can download it here: 36 | ECHO https://visualstudio.microsoft.com/downloads/ 37 | EXIT /B 1 38 | -------------------------------------------------------------------------------- /version.rc: -------------------------------------------------------------------------------- 1 | #include "winver.h" 2 | 3 | #define VER_COMMENT "Minecraft Classic server software\000" 4 | #define VER_PRODUCTNAME "CServer\000" 5 | #define VER_COPYRIGHT "Copyright (c) 2023 igor725\000" 6 | 7 | #define VER_FILEVER 0,6,0,1 8 | #define VER_FILEVER_STR "0.6.0.1\000" 9 | #define VER_FILENAME "server.exe\000" 10 | 11 | #define VER_PRODUCTVER 0,6,0,0 12 | #define VER_PRODUCTVER_STR "0.6.0\000" 13 | 14 | #ifdef DEBUG 15 | # define VER_DEBUG VS_FF_DEBUG 16 | #else 17 | # define VER_DEBUG 0 18 | #endif 19 | 20 | 1 VERSIONINFO 21 | FILEVERSION VER_FILEVER 22 | PRODUCTVERSION VER_PRODUCTVER 23 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 24 | FILEFLAGS (VS_FF_PRERELEASE|VER_DEBUG) 25 | FILEOS VOS_NT_WINDOWS32 26 | FILETYPE VFT_APP 27 | { 28 | BLOCK "StringFileInfo" 29 | { 30 | BLOCK "040904B0" 31 | { 32 | VALUE "FileDescription", VER_COMMENT 33 | VALUE "FileVersion", VER_FILEVER_STR 34 | VALUE "ProductName", VER_PRODUCTNAME 35 | VALUE "ProductVersion", VER_PRODUCTVER_STR 36 | VALUE "LegalCopyright", VER_COPYRIGHT 37 | VALUE "OriginalFilename", VER_FILENAME 38 | } 39 | } 40 | 41 | BLOCK "VarFileInfo" 42 | { 43 | VALUE "Translation", 0x0409, 1252 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/types/command.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMANDTYPE_H 2 | #define COMMANDTYPE_H 3 | #include "core.h" 4 | #include "str.h" 5 | #include "log.h" 6 | #include "strstor.h" 7 | #include "client.h" 8 | 9 | #define CMDF_NONE 0x00 10 | #define CMDF_OP BIT(0) 11 | #define CMDF_CLIENT BIT(1) 12 | #define CMDF_RESERVED0 BIT(2) 13 | #define CMDF_RESERVED1 BIT(3) 14 | #define CMDF_RESERVED2 BIT(4) 15 | 16 | /** 17 | * @brief Структура с информацией о вызове команды. 18 | * 19 | */ 20 | typedef struct _CommandCallData { 21 | struct _Command *const command; /** Вызванная команда */ 22 | cs_str const args; /** Аргументы с которыми команда была вызвана */ 23 | Client *const caller; /** Игрок, вызвавший команду, NULL в случае вызова из консоли*/ 24 | cs_char *const out; /* Массив, в который команда будет записывать вывод*/ 25 | } CommandCallData; 26 | 27 | /** 28 | * @brief Функция команды. 29 | * 30 | */ 31 | typedef cs_bool(*cmdFunc)(CommandCallData *); 32 | 33 | typedef struct _CommandRegBunch { 34 | cs_str name, descr; 35 | cmdFunc func; 36 | cs_byte flags; 37 | } CommandRegBunch; 38 | 39 | typedef struct _Command { 40 | cs_str name; 41 | cs_char alias[7]; 42 | cs_byte flags; 43 | cmdFunc func; 44 | cs_str descr; 45 | void *data; 46 | } Command; 47 | #endif 48 | -------------------------------------------------------------------------------- /src/groups.c: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | #include "str.h" 3 | #include "list.h" 4 | #include "platform.h" 5 | #include "groups.h" 6 | 7 | static KListField *headCGroup = NULL; 8 | 9 | cs_uintptr Groups_Create(cs_str name, cs_byte rank) { 10 | KListField *gptr; 11 | CGroup *tmpgrp; 12 | List_Iter(gptr, headCGroup) { 13 | tmpgrp = (CGroup *)gptr->value.ptr; 14 | if(String_CaselessCompare(tmpgrp->name, name)) 15 | return GROUPS_INVALID_ID; 16 | } 17 | 18 | cs_uintptr nextid = 0; 19 | if(headCGroup) 20 | nextid = headCGroup->key.numptr + 1; 21 | 22 | tmpgrp = Memory_Alloc(1, sizeof(CGroup)); 23 | String_Copy(tmpgrp->name, MAX_STR_LEN, name); 24 | tmpgrp->rank = rank; 25 | KList_AddField(&headCGroup, (void *)nextid, tmpgrp); 26 | return nextid; 27 | } 28 | 29 | CGroup *Groups_GetByID(cs_uintptr gid) { 30 | KListField *gptr = NULL; 31 | 32 | List_Iter(gptr, headCGroup) { 33 | if(gptr->key.numptr == gid) 34 | return (CGroup *)gptr->value.ptr; 35 | } 36 | 37 | return NULL; 38 | } 39 | 40 | cs_bool Groups_Remove(cs_uintptr gid) { 41 | KListField *gptr = NULL; 42 | 43 | List_Iter(gptr, headCGroup) { 44 | if(gptr->key.numptr == gid) { 45 | Memory_Free(gptr->value.ptr); 46 | KList_Remove(&headCGroup, gptr); 47 | return true; 48 | } 49 | } 50 | 51 | return false; 52 | } 53 | -------------------------------------------------------------------------------- /src/server.h: -------------------------------------------------------------------------------- 1 | #ifndef SERVER_H 2 | #define SERVER_H 3 | #include "core.h" 4 | #include "types/platform.h" 5 | #include "types/config.h" 6 | 7 | #define CFG_SERVERIP_KEY "server-ip" 8 | #define CFG_SERVERPORT_KEY "server-port" 9 | #define CFG_SERVERNAME_KEY "server-name" 10 | #define CFG_SERVERMOTD_KEY "server-motd" 11 | #define CFG_LOGLEVEL_KEY "log-level" 12 | #define CFG_SANITIZE_KEY "sanitize-names" 13 | #define CFG_IGNOREDEP_KEY "ignore-deprecation" 14 | #define CFG_LOCALOP_KEY "always-local-op" 15 | #define CFG_MAXPLAYERS_KEY "max-players" 16 | #define CFG_CONN_KEY "max-connections-per-ip" 17 | #define CFG_WORLDS_KEY "worlds-list" 18 | 19 | VAR cs_bool Server_Active, Server_Ready; 20 | VAR CStore *Server_Config; 21 | VAR cs_uint64 Server_StartTime; 22 | 23 | #ifndef CORE_BUILD_PLUGIN 24 | cs_bool Server_Init(void); 25 | void Server_StartLoop(void); 26 | void Server_Cleanup(void); 27 | #endif 28 | 29 | #define SERVERINFO_FLAG_DEBUG BIT(0) 30 | #define SERVERINFO_FLAG_WININET BIT(1) 31 | #define SERVERINFO_FLAG_LIBCURL BIT(2) 32 | #define SERVERINFO_FLAG_WINCRYPT BIT(3) 33 | #define SERVERINFO_FLAG_LIBCRYPTO BIT(4) 34 | 35 | typedef struct _ServerInfo { 36 | cs_int32 coreFlags; 37 | cs_str coreName; 38 | cs_str coreGitTag; 39 | } ServerInfo; 40 | 41 | API cs_bool Server_GetInfo(ServerInfo *info, cs_size sisz); 42 | #endif // SERVER_H 43 | -------------------------------------------------------------------------------- /src/tests/memory.c: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | #include "platform.h" 3 | #include "tests.h" 4 | 5 | cs_bool Tests_Memory(void) { 6 | Tests_NewTask("Try alloc memory"); 7 | unsigned char *mem, *mem2; 8 | Tests_Assert((mem = Memory_TryAlloc(1000, 1)) != NULL, "allocate first memory block"); 9 | Tests_Assert((mem2 = Memory_TryAlloc(1000, 1)) != NULL, "allocate second memory block"); 10 | 11 | Tests_NewTask("Check memory block size"); 12 | Tests_Assert(Memory_GetSize(mem) >= 1000, "check first memory block size"); 13 | Tests_Assert(Memory_GetSize(mem2) >= 1000, "check second memory block size"); 14 | 15 | Tests_NewTask("Try to realloc memory block"); 16 | Tests_Assert((mem = Memory_Realloc(mem, 100)) != NULL, "reallocate first memory block"); 17 | Tests_Assert((mem2 = Memory_Realloc(mem2, 100)) != NULL, "reallocate second memory block"); 18 | 19 | Tests_NewTask("Check reallocated memory block size"); 20 | Tests_Assert(Memory_GetSize(mem) >= 100, "check first memory block size"); 21 | Tests_Assert(Memory_GetSize(mem2) >= 100, "check second memory block size"); 22 | 23 | Tests_NewTask("Memory filling"); 24 | Memory_Fill(mem, 100, 0x66); 25 | for(int i = 0; i < 100; i++) Tests_Assert(mem[i] == 0x66, "test filled memory"); 26 | 27 | Tests_NewTask("Memory copying"); 28 | Memory_Copy(mem2, mem, 50); 29 | for(int i = 0; i < 100; i++) Tests_Assert(mem2[i] == (i < 50 ? mem[i] : 0x00), "compare copied memory"); 30 | 31 | Memory_Free(mem); 32 | Memory_Free(mem2); 33 | return true; 34 | } 35 | -------------------------------------------------------------------------------- /src/cpe.h: -------------------------------------------------------------------------------- 1 | #ifndef CPE_H 2 | #define CPE_H 3 | #include "core.h" 4 | #include "types/cpe.h" 5 | #include "types/client.h" 6 | #include "vector.h" 7 | 8 | #ifndef CORE_BUILD_PLUGIN 9 | CPEModel *CPE_GetModel(cs_byte id); 10 | void CPE_SendModel(Client *client, cs_int32 extVer, cs_byte id); 11 | void CPE_SendParticle(Client *client, cs_byte id); 12 | CPEParticle *CPE_GetParticle(cs_byte id); 13 | #endif 14 | 15 | API cs_bool CPE_IsModelDefined(cs_byte id); 16 | API cs_bool CPE_IsModelDefinedPtr(CPEModel *model); 17 | API cs_str CPE_GetDefaultModelName(void); 18 | API cs_bool CPE_DefineModel(cs_byte id, CPEModel *model); 19 | API cs_bool CPE_UndefineModel(cs_byte id); 20 | API cs_bool CPE_UndefineModelPtr(CPEModel *mdl); 21 | API cs_bool CPE_CheckModel(Client *client, cs_int16 model); 22 | API cs_int16 CPE_GetModelNum(cs_str model); 23 | API cs_uint32 CPE_GetModelStr(cs_int16 num, cs_char *buffer, cs_uint32 buflen); 24 | 25 | API cs_bool CPE_IsParticleDefined(cs_byte id); 26 | API cs_bool CPE_IsParticleDefinedPtr(CPEParticle *part); 27 | API cs_bool CPE_DefineParticle(cs_byte id, CPEParticle *part); 28 | API cs_bool CPE_UndefineParticle(cs_byte id); 29 | API cs_bool CPE_UndefineParticlePtr(CPEParticle *ptr); 30 | 31 | API void Cuboid_SetPositions(CPECuboid *cub, SVec start, SVec end); 32 | API void Cuboid_SetColor(CPECuboid *cub, Color4 color); 33 | API cs_byte Cuboid_GetID(CPECuboid *cub); 34 | API cs_uint32 Cuboid_GetSize(CPECuboid *cub); 35 | API void Cuboid_GetPositions(CPECuboid *cub, SVec *start, SVec *end); 36 | #endif 37 | -------------------------------------------------------------------------------- /src/csmath.c: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | #include "csmath.h" 3 | #include "platform.h" 4 | 5 | cs_float Math_Sqrt(cs_float f) { 6 | union { 7 | cs_int32 i; 8 | cs_float f; 9 | } u; 10 | 11 | u.f = f; 12 | u.i = (1 << 29) + (u.i >> 1) - (1 << 22); 13 | 14 | u.f = u.f + f / u.f; 15 | u.f = 0.25f * u.f + f / u.f; 16 | 17 | return u.f; 18 | } 19 | 20 | #define RND_VALUE (0x5DEECE66DULL) 21 | #define RND_MASK ((1ULL << 48) - 1) 22 | 23 | void Random_Seed(RNGState *seed, cs_int32 seedInit) { 24 | *seed = (seedInit ^ RND_VALUE) & RND_MASK; 25 | } 26 | 27 | void Random_SeedFromTime(RNGState *secrnd) { 28 | Random_Seed(secrnd, (cs_int32)Time_GetMSec()); 29 | } 30 | 31 | cs_int32 Random_Next(RNGState *seed, cs_int32 n) { 32 | cs_int64 raw; 33 | cs_int32 bits, val; 34 | 35 | if ((n & -n) == n) { /* i.e., n is a power of 2 */ 36 | *seed = (*seed * RND_VALUE + 0xBLL) & RND_MASK; 37 | raw = (cs_int64)(*seed >> (48 - 31)); 38 | return (cs_int32)((n * raw) >> 31); 39 | } 40 | 41 | do { 42 | *seed = (*seed * RND_VALUE + 0xBLL) & RND_MASK; 43 | bits = (cs_int32)(*seed >> (48 - 31)); 44 | val = bits % n; 45 | } while (bits - val + (n - 1) < 0); 46 | 47 | return val; 48 | } 49 | 50 | cs_float Random_Float(RNGState *seed) { 51 | *seed = (*seed * RND_VALUE + 0xBLL) & RND_MASK; 52 | cs_int32 raw = (cs_int32)(*seed >> (48 - 24)); 53 | return raw / ((cs_float)(1 << 24)); 54 | } 55 | 56 | cs_int32 Random_Range(RNGState *rnd, cs_int32 min, cs_int32 max) { 57 | return min + Random_Next(rnd, max + 1 - min); 58 | } 59 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | #include "platform.h" 3 | #include "str.h" 4 | #include "http.h" 5 | #include "server.h" 6 | #include "log.h" 7 | #include "cserror.h" 8 | #include "hash.h" 9 | #include "compr.h" 10 | #include "tests.h" 11 | 12 | INL static cs_bool Init(void) { 13 | return Memory_Init() && Log_Init() 14 | && Error_Init() && Socket_Init(); 15 | } 16 | 17 | int main(int argc, char *argv[]) { 18 | if(Init()) { 19 | cs_bool testmode = false, nochdir = false; 20 | 21 | if(argc > 1) { 22 | for(cs_int32 i = 1; i < argc; i++) { 23 | if(String_CaselessCompare(argv[i], "testmode")) 24 | testmode = true; 25 | else if(String_CaselessCompare(argv[i], "nochdir")) 26 | nochdir = true; 27 | } 28 | } 29 | 30 | if(!nochdir) { 31 | cs_char *lastSlash = String_LastChar(argv[0], *PATH_DELIM); 32 | if(lastSlash) { 33 | // Отсоединяем имя запускаемого файла и 34 | // тем самым получаем путь до него. 35 | *lastSlash = '\0'; 36 | Directory_SetCurrentDir(argv[0]); 37 | } 38 | } 39 | 40 | if(testmode) { 41 | if(Tests_PerformAll()) { 42 | Log_Info("All tests passed!"); 43 | Process_Exit(0); 44 | } else { 45 | Log_Error("Some tests failed!"); 46 | Process_Exit(1); 47 | } 48 | } else if(Server_Init()) 49 | Server_StartLoop(); 50 | 51 | Server_Cleanup(); 52 | Compr_Uninit(); 53 | Http_Uninit(); 54 | Hash_Uninit(); 55 | Socket_Uninit(); 56 | Error_Uninit(); 57 | Log_Uninit(); 58 | Memory_Uninit(); 59 | return 0; 60 | } 61 | 62 | return Error_GetSysCode(); 63 | } 64 | -------------------------------------------------------------------------------- /src/tests/config.c: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | #include "config.h" 3 | #include "tests.h" 4 | 5 | cs_bool Tests_Config(void) { 6 | Tests_NewTask("Create config store"); 7 | CStore *store; 8 | CEntry *ent; 9 | Tests_Assert((store = Config_NewStore("__test")) != NULL, "create config store"); 10 | Tests_Assert((ent = Config_NewEntry(store, "test-key", CONFIG_TYPE_BOOL)) != NULL, "create boolean entry"); 11 | Tests_Assert(Config_TypeNameToEnum(Config_TypeName(ent->type)) == ent->type, ""); 12 | Config_SetComment(ent, "_test_CoMment1"); 13 | Tests_Assert(Config_SetDefaultBool(ent, true), "set default boolean value"); 14 | 15 | Tests_Assert((ent = Config_NewEntry(store, "test-key-i32", CONFIG_TYPE_INT)) != NULL, "create i32 entry"); 16 | Tests_Assert(Config_TypeNameToEnum(Config_TypeName(ent->type)) == ent->type, ""); 17 | Config_SetComment(ent, "_test_CoMment1-i32_"); 18 | Config_SetDefaultInt(ent, 40); 19 | Config_SetLimit(ent, 10, 80); 20 | 21 | Tests_NewTask("Check main config module functions"); 22 | ent = Config_GetEntry(store, "test-key"); 23 | Tests_Assert(Config_SetDefaultStr(ent, "true") == false, "set default string value to boolean entry"); 24 | Tests_Assert(Config_SetStr(ent, "true") == false, "set string value to boolean entry"); 25 | Tests_Assert(Config_SetDefaultInt(ent, 40) == false, "set default int32 value to boolean entry"); 26 | Tests_Assert(Config_SetLimit(ent, 10, 20) == false, "set limit for boolean entry"); 27 | 28 | Tests_NewTask("Save config store"); 29 | Tests_Assert(Config_Save(store, true), "save config store"); 30 | return true; 31 | } 32 | -------------------------------------------------------------------------------- /src/types/websock.h: -------------------------------------------------------------------------------- 1 | #ifndef WEBSOCKTYPES_H 2 | #define WEBSOCKTYPES_H 3 | #include "core.h" 4 | #include "types/platform.h" 5 | 6 | #define WEBSHAKE_FLAG_UPGOK BIT(0) 7 | #define WEBSHAKE_FLAG_VEROK BIT(1) 8 | #define WEBSHAKE_FLAG_KEYOK BIT(2) 9 | #define WEBSHAKE_FLAGS_OK (WEBSHAKE_FLAG_UPGOK | WEBSHAKE_FLAG_VEROK | WEBSHAKE_FLAG_KEYOK) 10 | #define WEBSOCK_FRAME_MAXSIZE 65535 11 | 12 | typedef enum _EWebShakeState { 13 | WEBSHAKE_STATE_HTTP, 14 | WEBSHAKE_STATE_HEADERS, 15 | WEBSHAKE_STATE_FINISHING, 16 | WEBSHAKE_STATE_DONE 17 | } EWebShakeState; 18 | 19 | typedef enum _EWebSockState { 20 | WEBSOCK_STATE_HANDSHAKE, 21 | WEBSOCK_STATE_HEADER, 22 | WEBSOCK_STATE_LENGTH, 23 | WEBSOCK_STATE_MASK, 24 | WEBSOCK_STATE_PAYLD, 25 | WEBSOCK_STATE_DONE 26 | } EWebSockState; 27 | 28 | typedef enum _EWebSockErrors { 29 | WEBSOCK_ERROR_SUCC, 30 | WEBSOCK_ERROR_SOCKET, 31 | WEBSOCK_ERROR_STRLEN, 32 | WEBSOCK_ERROR_CONTINUE, 33 | WEBSOCK_ERROR_HTTPVER, 34 | WEBSOCK_ERROR_INVKEY, 35 | WEBSOCK_ERROR_PROTOVER, 36 | WEBSOCK_ERROR_NOTWS, 37 | WEBSOCK_ERROR_HASHINIT, 38 | WEBSOCK_ERROR_HEADER, 39 | WEBSOCK_ERROR_MASK, 40 | WEBSOCK_ERROR_PAYLOAD_TOO_BIG, 41 | } EWebSockErrors; 42 | 43 | typedef struct _WebShake { 44 | EWebShakeState state; 45 | cs_char line[1024]; 46 | cs_char key[32]; 47 | cs_int32 keylen; 48 | cs_int16 hdrflags; 49 | } WebShake; 50 | 51 | typedef struct _WebSock { 52 | WebShake shake; 53 | EWebSockState state; 54 | EWebSockErrors error; 55 | cs_uint16 paylen; 56 | cs_uint16 maxpaylen; 57 | cs_str proto; 58 | cs_char *payload; 59 | cs_char mask[4]; 60 | cs_byte opcode; 61 | cs_bool done; 62 | } WebSock; 63 | #endif 64 | -------------------------------------------------------------------------------- /src/timer.c: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | #include "platform.h" 3 | #include "timer.h" 4 | #include "list.h" 5 | 6 | static AListField *headTimer = NULL; 7 | 8 | Timer *Timer_Add(cs_int32 ticks, cs_uint32 delay, TimerCallback callback, void *ud) { 9 | AListField *field; 10 | List_Iter(field, headTimer) { 11 | Timer *htimer = (Timer *)field->value.ptr; 12 | if(htimer->callback == callback) 13 | return NULL; 14 | } 15 | Timer *timer = Memory_Alloc(1, sizeof(Timer)); 16 | timer->left = ticks; 17 | timer->delay = delay; 18 | timer->callback = callback; 19 | timer->userdata = ud; 20 | AList_AddField(&headTimer, timer); 21 | return timer; 22 | } 23 | 24 | INL static void removeTimerByField(AListField *field) { 25 | if(field->value.ptr) { 26 | Memory_Free(field->value.ptr); 27 | AList_Remove(&headTimer, field); 28 | } 29 | } 30 | 31 | void Timer_Remove(Timer *timer) { 32 | AListField *field; 33 | List_Iter(field, headTimer) { 34 | if(timer == field->value.ptr) { 35 | removeTimerByField(field); 36 | break; 37 | } 38 | } 39 | } 40 | 41 | void Timer_Update(cs_int32 delta) { 42 | AListField *field; 43 | List_Iter(field, headTimer) { 44 | Timer *timer = field->value.ptr; 45 | timer->nexttick -= delta; 46 | if(timer->nexttick <= 0) { 47 | timer->nexttick = timer->delay; 48 | if(timer->left != -1) --timer->left; 49 | timer->callback(++timer->ticks, timer->left, timer->userdata); 50 | if(timer->left == 0) 51 | removeTimerByField(field); 52 | } 53 | } 54 | } 55 | 56 | void Timer_RemoveAll(void) { 57 | while(headTimer) { 58 | Memory_Free(headTimer->value.ptr); 59 | AList_Remove(&headTimer, headTimer); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Launch debug", 6 | "type": "cppvsdbg", 7 | "request": "launch", 8 | "program": "${workspaceFolder}/out/x64dbg/server.exe", 9 | "stopAtEntry": false, 10 | "cwd": "${workspaceFolder}/out/x64dbg", 11 | "console": "integratedTerminal", 12 | // "preLaunchTask": "Build debug" 13 | }, 14 | { 15 | "name": "Launch testmode", 16 | "type": "cppvsdbg", 17 | "request": "launch", 18 | "program": "${workspaceFolder}/out/x64dbg/server.exe", 19 | "args": ["testmode"], 20 | "stopAtEntry": false, 21 | "cwd": "${workspaceFolder}/out/x64dbg", 22 | "console": "integratedTerminal", 23 | "preLaunchTask": "Build debug" 24 | }, 25 | { 26 | "name": "Launch debug (Linux)", 27 | "type": "cppdbg", 28 | "request": "launch", 29 | "program": "${workspaceFolder}/out/x86_64-linux-gnu-dbg/server", 30 | "stopAtEntry": false, 31 | "cwd": "${workspaceFolder}/out/x86_64-linux-gnu-dbg", 32 | "externalConsole": false, 33 | "MIMode": "gdb", 34 | "setupCommands": [ 35 | { 36 | "description": "Enable pretty-printing for gdb", 37 | "text": "-enable-pretty-printing", 38 | "ignoreFailures": true 39 | } 40 | ], 41 | // "preLaunchTask": "Build debug" 42 | }, 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /src/types/world.h: -------------------------------------------------------------------------------- 1 | #ifndef WORLDTYPES_H 2 | #define WORLDTYPES_H 3 | #include "core.h" 4 | #include "vector.h" 5 | #include "types/list.h" 6 | #include "types/platform.h" 7 | #include "types/compr.h" 8 | #include "types/cpe.h" 9 | 10 | #define WORLD_FLAG_NONE 0x00 11 | #define WORLD_FLAG_LOADED BIT(0) 12 | #define WORLD_FLAG_MODIFIED BIT(1) 13 | #define WORLD_FLAG_MODIGNORE BIT(2) 14 | #define WORLD_FLAG_INMEMORY BIT(3) 15 | 16 | #define WORLD_MAX_SIZE 4000000000u 17 | #define WORLD_INVALID_OFFSET (cs_uint32)-1 18 | 19 | typedef enum _EWorldError { 20 | WORLD_ERROR_SUCCESS = 0, 21 | WORLD_ERROR_IOFAIL, 22 | WORLD_ERROR_COMPR, 23 | WORLD_ERROR_INFOREAD, 24 | WORLD_ERROR_DATAREAD, 25 | WORLD_ERROR_INMEMORY 26 | } EWorldError; 27 | 28 | typedef enum _EWorldExtra { 29 | WORLD_EXTRA_NOINFO = 0, 30 | WORLD_EXTRA_UNKNOWN_DATA_TYPE, 31 | WORLD_EXTRA_IO_OPEN, 32 | WORLD_EXTRA_IO_WRITE, 33 | WORLD_EXTRA_IO_RENAME, 34 | WORLD_EXTRA_COMPR_INIT, 35 | WORLD_EXTRA_COMPR_PROC 36 | } EWorldExtra; 37 | 38 | typedef struct _WorldInfo { 39 | SVec dimensions; 40 | BlockDef *bdefines[256]; 41 | Color3 colors[WORLD_COLORS_COUNT]; 42 | cs_int32 props[WORLD_PROPS_COUNT]; 43 | cs_char texturepack[MAX_STR_LEN]; 44 | Vec spawnVec; 45 | Ang spawnAng; 46 | cs_int8 weatherType; 47 | cs_byte modval, modclr; 48 | cs_uint16 modprop; 49 | cs_uint32 seed; 50 | } WorldInfo; 51 | 52 | typedef struct _World { 53 | cs_uint32 flags; 54 | cs_str name; 55 | WorldInfo info; 56 | Mutex *mtx; 57 | Waitable *prgw; 58 | Waitable *taskw; 59 | cs_uint32 taskc; 60 | Compr compr; 61 | KListField *headNode; 62 | struct _WorldError { 63 | EWorldError code; 64 | EWorldExtra extra; 65 | } error; 66 | struct _WorldData { 67 | cs_uint32 size; 68 | void *ptr; 69 | BlockID *blocks; 70 | } wdata; 71 | } World; 72 | #endif 73 | -------------------------------------------------------------------------------- /src/generators.h: -------------------------------------------------------------------------------- 1 | #ifndef GENERATORS_H 2 | #define GENERATORS_H 3 | #include "core.h" 4 | #include "types/world.h" 5 | 6 | #define GENERATOR_SEED_FROM_TIME (cs_uint32)-1 7 | 8 | typedef cs_bool(*GeneratorRoutine)(World *world, cs_uint32 seed); 9 | 10 | #ifndef CORE_BUILD_PLUGIN 11 | cs_bool Generators_Init(void); 12 | void Generators_UnregisterAll(void); 13 | #endif 14 | 15 | /** 16 | * @brief Добавляет новый генератор миров. 17 | * 18 | * @param name название генератора 19 | * @param gr функция генератора 20 | * @return true - генератор зарегистрирован, false - генератор с таким именем уже существует 21 | */ 22 | API cs_bool Generators_Add(cs_str name, GeneratorRoutine gr); 23 | 24 | /** 25 | * @brief Удаляет добавленный ранее генератор миров. 26 | * 27 | * @param name название генератора 28 | * @return true - генератор удалён, false - генератор с таким именем не был зарегистрирован 29 | */ 30 | API cs_bool Generators_Remove(cs_str name); 31 | 32 | /** 33 | * @brief Удаляет добавленный ранее генератор миров. 34 | * 35 | * @param gr функция генератора 36 | * @return true - генератор удалён, false - генератор с такой функцией не был зарегистрирован 37 | */ 38 | API cs_bool Generators_RemoveByFunc(GeneratorRoutine gr); 39 | 40 | /** 41 | * @brief Применяет к указанному миру генератор. 42 | * 43 | * @param world указатель на мир 44 | * @param name имя генератора 45 | * @param data пока что не используется 46 | * @return true - генератор выполнил работу успешно, false - что-то пошло не так 47 | */ 48 | API cs_bool Generators_Use(World *world, cs_str name, cs_int32 seed); 49 | 50 | /** 51 | * @brief Возвращает функцию генератора по его имени. 52 | * 53 | * @param name имя генератора 54 | * @return функция генератора 55 | */ 56 | API GeneratorRoutine Generators_Get(cs_str name); 57 | #endif // GENERATORS_H 58 | -------------------------------------------------------------------------------- /src/websock.h: -------------------------------------------------------------------------------- 1 | #ifndef WEBSOCKET_H 2 | #define WEBSOCKET_H 3 | #include "core.h" 4 | #include "types/websock.h" 5 | #include "types/netbuffer.h" 6 | 7 | /** 8 | * @brief Эдакий комбайн, выполняет хендшейк и чтение из 9 | * сокета фреймов. Рассчитан на работу с нонблок сокетами. 10 | * Внимание: Может вернуть false даже если сокет в порядке! 11 | * В случае ошибки всегда проверяйте WebSock_GetErrorCode(), 12 | * если там значение WEBSOCK_ERROR_CONTINUE, то это значит, 13 | * что нужен ещё один вызов, чтобы получить фрейм полностью. 14 | * 15 | * @param ws указатель на структуру вебсокета 16 | * @param sock сокет, из которого читаем данные 17 | * @return true - фрейм полностью прочитан, 18 | * false - что-то не так 19 | */ 20 | API cs_bool WebSock_Tick(WebSock *ws, NetBuffer *sock); 21 | 22 | /** 23 | * @brief Отправляет заголовок фрейма клиенту. 24 | * За заголовком должны следовать сырые данные 25 | * указанного размера @len. 26 | * 27 | * @param sock сокет, в который нужно послать фрейм 28 | * @param opcode опкод фрейма 29 | * @param buf данные, которые будут записаны в фрейм 30 | * @param len длинна данных 31 | * @return true - фрейм отправлен успешно, 32 | * false - похоже, что соединение разорвано. 33 | */ 34 | API cs_int32 WebSock_WriteHeader(Socket sock, cs_byte opcode, cs_uint32 len, cs_int32 *hdrlen); 35 | 36 | /** 37 | * @brief Возвращает код последней произошедшей ошибки. 38 | * 39 | * @param ws указатель на структуру вебсокета 40 | * @return код ошибки из перечисления EWebSockErrors 41 | */ 42 | API EWebSockErrors WebSock_GetErrorCode(WebSock *ws); 43 | 44 | /** 45 | * @brief Делает тоже самое, что и WebSock_GetErrorCode, 46 | * но она уже возвращает ошибку в качестве строки. 47 | * 48 | * @param ws указатель на структуру вебсокета 49 | * @return строка с ошибкой 50 | */ 51 | API cs_str WebSock_GetError(WebSock *ws); 52 | #endif // WEBSOCKET_H 53 | -------------------------------------------------------------------------------- /src/types/platform.h: -------------------------------------------------------------------------------- 1 | #ifndef PLATFORMTYPES_H 2 | #define PLATFORMTYPES_H 3 | #include "core.h" 4 | #include 5 | 6 | #if defined(CORE_USE_WINDOWS) 7 | # define _WINSOCK_DEPRECATED_NO_WARNINGS 8 | # include 9 | # define TSHND_OK TRUE 10 | # define MSG_NOSIGNAL 0 11 | # define MSG_DONTWAIT 0 12 | 13 | typedef WIN32_FIND_DATAA ITER_FILE; 14 | typedef cs_ulong TRET, TSHND_PARAM; 15 | typedef void Waitable; 16 | typedef CRITICAL_SECTION Mutex; 17 | typedef SOCKET Socket; 18 | typedef HANDLE Thread, ITER_DIR; 19 | typedef BOOL TSHND_RET; 20 | #elif defined(CORE_USE_UNIX) 21 | # include 22 | # include 23 | # include 24 | # include 25 | # include 26 | # include 27 | # include 28 | # include 29 | # include 30 | # include 31 | # define INVALID_SOCKET (Socket)-1 32 | # define SD_SEND SHUT_WR 33 | # define TSHND_OK 34 | 35 | typedef DIR *ITER_DIR; 36 | typedef struct dirent *ITER_FILE; 37 | typedef void *TRET; 38 | typedef pthread_t Thread; 39 | typedef struct _UMutex { 40 | pthread_mutex_t handle; 41 | pthread_mutexattr_t attr; 42 | } Mutex; 43 | typedef void TSHND_RET; 44 | typedef cs_int32 TSHND_PARAM; 45 | typedef struct _Waitable { 46 | pthread_cond_t cond; 47 | Mutex *mutex; 48 | cs_bool signalled; 49 | } Waitable; 50 | typedef cs_int32 Socket; 51 | #endif 52 | 53 | typedef cs_int32 cs_error; 54 | typedef FILE *cs_file; 55 | typedef void *TARG; 56 | typedef TRET(*TFUNC)(TARG); 57 | typedef TSHND_RET(*TSHND)(TSHND_PARAM); 58 | 59 | typedef enum _EIterState { 60 | ITER_INITIAL, 61 | ITER_READY, 62 | ITER_DONE, 63 | ITER_ERROR 64 | } EIterState; 65 | 66 | typedef struct _DirIter { 67 | EIterState state; 68 | cs_char fmt[256]; 69 | cs_str cfile; 70 | cs_bool isDir; 71 | ITER_DIR dirHandle; 72 | ITER_FILE fileHandle; 73 | } DirIter; 74 | #endif 75 | -------------------------------------------------------------------------------- /src/list.c: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | #include "platform.h" 3 | #include "list.h" 4 | 5 | AListField *AList_AddField(AListField **head, void *value) { 6 | AListField *ptr = Memory_Alloc(1, sizeof(AListField)); 7 | ptr->value.ptr = value; 8 | if(*head) (*head)->next = ptr; 9 | ptr->prev = *head; 10 | *head = ptr; 11 | return ptr; 12 | } 13 | 14 | cs_bool AList_Iter(AListField **head, void *ud, cs_bool(*callback)(AListField *, AListField **, void *)) { 15 | AListField *tmp; 16 | 17 | List_Iter(tmp, (*head)) 18 | if(!callback(tmp, head, ud)) return false; 19 | 20 | return true; 21 | } 22 | 23 | UMultiValue AList_GetValue(AListField *field) { 24 | return field->value; 25 | } 26 | 27 | void AList_Remove(AListField **head, AListField *field) { 28 | if(field->next) 29 | field->next->prev = field->prev; 30 | else 31 | *head = field->prev; 32 | if(field->prev) 33 | field->prev->next = field->next; 34 | Memory_Free(field); 35 | } 36 | 37 | KListField *KList_AddField(KListField **head, void *key, void *value) { 38 | KListField *ptr = Memory_Alloc(1, sizeof(KListField)); 39 | ptr->key.ptr = key; 40 | ptr->value.ptr = value; 41 | if(*head) (*head)->next = ptr; 42 | ptr->prev = *head; 43 | *head = ptr; 44 | return ptr; 45 | } 46 | 47 | cs_bool KList_Iter(KListField **head, void *ud, cs_bool(*callback)(KListField *, KListField **, void *)) { 48 | KListField *tmp = NULL; 49 | 50 | List_Iter(tmp, (*head)) 51 | if(!callback(tmp, head, ud)) return false; 52 | 53 | return true; 54 | } 55 | 56 | UMultiValue KList_GetKey(KListField *field) { 57 | return field->key; 58 | } 59 | 60 | UMultiValue KList_GetValue(KListField *field) { 61 | return field->value; 62 | } 63 | 64 | void KList_Remove(KListField **head, KListField *field) { 65 | if(field->next) 66 | field->next->prev = field->prev; 67 | else 68 | *head = field->prev; 69 | if(field->prev) 70 | field->prev->next = field->next; 71 | Memory_Free(field); 72 | } 73 | -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_H 2 | #define CONFIG_H 3 | #include "core.h" 4 | #include "types/config.h" 5 | 6 | API ECError Config_PopError(CStore *store, ECExtra *extra, cs_int32 *line); 7 | 8 | API cs_str Config_TypeName(ECTypes type); 9 | API ECTypes Config_TypeNameToEnum(cs_str name); 10 | API cs_str Config_ErrorToString(ECError code); 11 | API cs_str Config_ExtraToString(ECExtra extra); 12 | 13 | API CStore *Config_NewStore(cs_str name); 14 | API void Config_EmptyStore(CStore *store); 15 | API void Config_DestroyStore(CStore *store); 16 | API void Config_ResetToDefault(CStore *store); 17 | API void Config_SetGeneric(CEntry *ent, cs_str value); 18 | API void Config_SetToDefault(CEntry *ent); 19 | API cs_int32 Config_Parse(CEntry *ent, cs_char *buf, cs_size len); 20 | 21 | API CEntry *Config_NewEntry(CStore *store, cs_str key, ECTypes type); 22 | API CEntry *Config_GetEntry(CStore *store, cs_str key); 23 | API ECTypes Config_GetEntryType(CEntry *ent); 24 | API cs_str Config_GetEntryTypeName(CEntry *ent); 25 | API cs_str Config_GetEntryKey(CEntry *ent); 26 | 27 | API cs_bool Config_Load(CStore *store); 28 | API cs_bool Config_Save(CStore *store, cs_bool force); 29 | 30 | API void Config_SetComment(CEntry *ent, cs_str commentary); 31 | API cs_bool Config_SetLimit(CEntry *ent, cs_int32 min, cs_int32 max); 32 | 33 | API cs_int32 Config_GetInt(CEntry *ent); 34 | API cs_int32 Config_GetIntByKey(CStore *store, cs_str key); 35 | 36 | API cs_bool Config_SetDefaultInt(CEntry *ent, cs_int32 value); 37 | API cs_bool Config_SetInt(CEntry *ent, cs_int32 value); 38 | 39 | API cs_str Config_GetStr(CEntry *ent); 40 | API cs_str Config_GetStrByKey(CStore *store, cs_str key); 41 | API cs_bool Config_SetDefaultStr(CEntry *ent, cs_str value); 42 | API cs_bool Config_SetStr(CEntry *ent, cs_str value); 43 | 44 | API cs_bool Config_GetBool(CEntry *ent); 45 | API cs_bool Config_GetBoolByKey(CStore *store, cs_str key); 46 | API cs_bool Config_SetDefaultBool(CEntry *ent, cs_bool value); 47 | API cs_bool Config_SetBool(CEntry *ent, cs_bool value); 48 | #endif // CONFIG_H 49 | -------------------------------------------------------------------------------- /src/types/config.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIGTYPES_H 2 | #define CONFIGTYPES_H 3 | #include "core.h" 4 | 5 | typedef enum _ECError { 6 | CONFIG_ERROR_SUCCESS = 0, 7 | CONFIG_ERROR_INTERNAL, 8 | CONFIG_ERROR_IOFAIL, 9 | CONFIG_ERROR_PARSE, 10 | 11 | CONFIG_MAX_ERROR 12 | } ECError; 13 | 14 | typedef enum _ECExtra { 15 | CONFIG_EXTRA_NOINFO = 0, 16 | CONFIG_EXTRA_IO_LINEASERROR, 17 | CONFIG_EXTRA_IO_FRENAME, 18 | CONFIG_EXTRA_PARSE_LINEFORMAT, 19 | CONFIG_EXTRA_PARSE_NUMBER, 20 | CONFIG_EXTRA_PARSE_END, 21 | 22 | CONFIG_MAX_EXTRA 23 | } ECExtra; 24 | 25 | typedef enum _ECTypes { 26 | CONFIG_TYPE_BOOL, 27 | CONFIG_TYPE_INT, 28 | CONFIG_TYPE_STR, 29 | 30 | CONFIG_MAX_TYPE 31 | } ECTypes; 32 | 33 | typedef union _CUValue { 34 | cs_bool vbool; 35 | cs_int32 vint; 36 | cs_str vstr; 37 | } CUValue; 38 | 39 | #define CFG_MAX_LEN 255 // Максимальная длина строки в cfg файле 40 | #define CFG_FREADED BIT(0) // Была ли осуществленна попытка чтения значения из cfg файла 41 | #define CFG_FCHANGED BIT(1) // Отличается ли текущее значение записи от заданного стандартного 42 | #define CFG_FHAVELIMITS BIT(2) // Применимо только для integer типов 43 | 44 | typedef struct _CEntry { 45 | ECTypes type; // Тип cfg-записи 46 | cs_byte flags; // Флаги cfg-записи 47 | cs_str commentary; // Комментарий к записи 48 | cs_str key; // Ключ, присваиваемый записи при создании 49 | CUValue value, defvalue; // Значение записи, заданное пользователем 50 | cs_int32 limits[2]; // Минимальный и максимальный предел значений записи 51 | struct _CEntry *next; // Следующая запись 52 | struct _CStore *store; // Cfg-хранилище, которому принадлежит запись 53 | } CEntry; 54 | 55 | typedef struct _CStore { 56 | cs_str name; // Название cfg-файла 57 | cs_bool modified; // Было ли хранилище модифицировано во время работы сервера 58 | struct _CError { 59 | ECError code; 60 | ECExtra extra; 61 | cs_int32 line; 62 | } error; // Информация о ошибках 63 | CEntry *firstCfgEntry, // Первая запись в хранилище 64 | *lastCfgEntry; // Последняя запись в хранилище 65 | } CStore; 66 | #endif 67 | -------------------------------------------------------------------------------- /src/hash.h: -------------------------------------------------------------------------------- 1 | #ifndef HASH_H 2 | #define HASH_H 3 | #include "core.h" 4 | 5 | #ifndef CORE_BUILD_PLUGIN 6 | void Hash_Uninit(void); 7 | #endif 8 | 9 | #if defined(HASH_USE_WINCRYPT_BACKEND) 10 | // Start of 11 | # define HP_HASHVAL 0x0002 12 | # define MS_DEF_PROV "Microsoft Base Cryptographic Provider v1.0" 13 | # define CRYPT_VERIFYCONTEXT 0xF0000000 14 | # define PROV_RSA_FULL 1 15 | # define ALG_SID_MD5 3 16 | # define ALG_SID_SHA1 4 17 | # define ALG_CLASS_HASH (4 << 13) 18 | # define CALG_MD5 (ALG_CLASS_HASH | ALG_SID_MD5) 19 | # define CALG_SHA1 (ALG_CLASS_HASH | ALG_SID_SHA1) 20 | typedef cs_uintptr HCRYPTHASH, HCRYPTPROV, HCRYPTKEY; 21 | typedef cs_uint32 ALG_ID; 22 | typedef cs_int32 BOOL; 23 | // End of 24 | 25 | typedef struct _WinHash { 26 | HCRYPTHASH hash; 27 | cs_ulong hashLen; 28 | } SHA_CTX, MD5_CTX, HASH_CTX; 29 | #elif defined(HASH_USE_CRYPTO_BACKEND) 30 | // Start of 31 | # define MD5_LONG unsigned int 32 | # define MD5_CBLOCK 64 33 | # define MD5_LBLOCK (MD5_CBLOCK/4) 34 | 35 | typedef struct MD5state_st { 36 | MD5_LONG A, B, C, D; 37 | MD5_LONG Nl, Nh; 38 | MD5_LONG data[MD5_LBLOCK]; 39 | unsigned int num; 40 | } MD5_CTX; 41 | // End of 42 | 43 | // Start of 44 | # define SHA_LONG unsigned int 45 | 46 | # define SHA_LBLOCK 16 47 | # define SHA_CBLOCK (SHA_LBLOCK*4) 48 | # define SHA_LAST_BLOCK (SHA_CBLOCK-8) 49 | 50 | typedef struct SHAstate_st { 51 | SHA_LONG h0, h1, h2, h3, h4; 52 | SHA_LONG Nl, Nh; 53 | SHA_LONG data[SHA_LBLOCK]; 54 | unsigned int num; 55 | } SHA_CTX; 56 | // End of 57 | #else 58 | # error No сryptographic backend selected 59 | #endif 60 | 61 | API cs_bool SHA1_Start(SHA_CTX *ctx); 62 | API cs_bool SHA1_PushData(SHA_CTX *ctx, const void *data, cs_ulong len); 63 | API cs_bool SHA1_End(cs_byte *hash, SHA_CTX *ctx); 64 | 65 | API cs_bool MD5_Start(MD5_CTX *ctx); 66 | API cs_bool MD5_PushData(MD5_CTX *ctx, const void *data, cs_ulong len); 67 | API cs_bool MD5_End(cs_byte *hash, MD5_CTX *ctx); 68 | #endif // HASH_H 69 | -------------------------------------------------------------------------------- /src/generators.c: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | #include "str.h" 3 | #include "list.h" 4 | #include "generators.h" 5 | 6 | static cs_bool flatgenerator(World *, cs_uint32); 7 | static cs_bool normalgenerator(World *, cs_uint32); 8 | 9 | #include "generators/flat.c" 10 | #include "generators/normal.c" 11 | 12 | static KListField *headGenerator = NULL; 13 | struct _GenRoutineStruct {GeneratorRoutine func;}; 14 | 15 | cs_bool Generators_Init(void) { 16 | return Generators_Add("flat", flatgenerator) && 17 | Generators_Add("normal", normalgenerator); 18 | } 19 | 20 | cs_bool Generators_Add(cs_str name, GeneratorRoutine gr) { 21 | KListField *tmp; 22 | List_Iter(tmp, headGenerator) 23 | if(String_CaselessCompare(tmp->key.str, name)) return false; 24 | 25 | KList_AddField(&headGenerator, (void *)name, (void *)gr); 26 | return true; 27 | } 28 | 29 | cs_bool Generators_Remove(cs_str name) { 30 | KListField *ptr = NULL; 31 | 32 | List_Iter(ptr, headGenerator) { 33 | if(String_CaselessCompare(ptr->key.str, name)) { 34 | KList_Remove(&headGenerator, ptr); 35 | return true; 36 | } 37 | } 38 | 39 | return false; 40 | } 41 | 42 | cs_bool Generators_RemoveByFunc(GeneratorRoutine gr) { 43 | KListField *ptr = NULL; 44 | 45 | List_Iter(ptr, headGenerator) { 46 | struct _GenRoutineStruct *grs = (struct _GenRoutineStruct *)&ptr->value.ptr; 47 | if(grs && grs->func == gr) { 48 | KList_Remove(&headGenerator, ptr); 49 | return true; 50 | } 51 | } 52 | 53 | return false; 54 | } 55 | 56 | cs_bool Generators_Use(World *world, cs_str name, cs_int32 seed) { 57 | GeneratorRoutine gr = Generators_Get(name); 58 | return gr != NULL ? gr(world, seed) : false; 59 | } 60 | 61 | GeneratorRoutine Generators_Get(cs_str name) { 62 | KListField *ptr = NULL; 63 | 64 | List_Iter(ptr, headGenerator) { 65 | if(String_CaselessCompare(ptr->key.str, name)) { 66 | struct _GenRoutineStruct *grs = (struct _GenRoutineStruct *)&ptr->value.ptr; 67 | if(grs) return grs->func; 68 | break; 69 | } 70 | } 71 | 72 | return NULL; 73 | } 74 | 75 | void Generators_UnregisterAll(void) { 76 | while(headGenerator) 77 | KList_Remove(&headGenerator, headGenerator); 78 | } 79 | -------------------------------------------------------------------------------- /src/types/keys.h: -------------------------------------------------------------------------------- 1 | #ifndef CSKEYS_H 2 | #define CSKEYS_H 3 | typedef enum _ELWJGLKey { 4 | LWKEY_ESCAPE = 1, 5 | LWKEY_1, 6 | LWKEY_2, 7 | LWKEY_3, 8 | LWKEY_4, 9 | LWKEY_5, 10 | LWKEY_6, 11 | LWKEY_7, 12 | LWKEY_8, 13 | LWKEY_9, 14 | LWKEY_0, 15 | LWKEY_MINUS, 16 | LWKEY_EQUALS, 17 | LWKEY_BACKSPACE, 18 | LWKEY_TAB, 19 | LWKEY_Q, 20 | LWKEY_W, 21 | LWKEY_E, 22 | LWKEY_R, 23 | LWKEY_T, 24 | LWKEY_Y, 25 | LWKEY_U, 26 | LWKEY_I, 27 | LWKEY_O, 28 | LWKEY_P, 29 | LWKEY_LBRACKET, 30 | LWKEY_RBRACKET, 31 | LWKEY_ENTER, 32 | LWKEY_LCTRL, 33 | LWKEY_A, 34 | LWKEY_S, 35 | LWKEY_D, 36 | LWKEY_F, 37 | LWKEY_G, 38 | LWKEY_H, 39 | LWKEY_J, 40 | LWKEY_K, 41 | LWKEY_L, 42 | LWKEY_SEMICOLON, 43 | LWKEY_QUOTE, 44 | LWKEY_TILDE, 45 | LWKEY_LSHIFT, 46 | LWKEY_BACKSLASH, 47 | LWKEY_Z, 48 | LWKEY_X, 49 | LWKEY_C, 50 | LWKEY_V, 51 | LWKEY_B, 52 | LWKEY_N, 53 | LWKEY_M, 54 | LWKEY_COMMA, 55 | LWKEY_PERIOD, 56 | LWKEY_SLASH, 57 | LWKEY_RSHIFT, 58 | LWKEY_LALT = 56, 59 | LWKEY_SPACE, 60 | LWKEY_CAPSLOCK, 61 | LWKEY_F1, 62 | LWKEY_F2, 63 | LWKEY_F3, 64 | LWKEY_F4, 65 | LWKEY_F5, 66 | LWKEY_F6, 67 | LWKEY_F7, 68 | LWKEY_F8, 69 | LWKEY_F9, 70 | LWKEY_F10, 71 | LWKEY_NUMLOCK, 72 | LWKEY_SCROLLLOCK, 73 | LWKEY_KP7, 74 | LWKEY_KP8, 75 | LWKEY_KP9, 76 | LWKEY_KP_MINUS, 77 | LWKEY_KP4, 78 | LWKEY_KP5, 79 | LWKEY_KP6, 80 | LWKEY_KP_PLUS, 81 | LWKEY_KP1, 82 | LWKEY_KP2, 83 | LWKEY_KP3, 84 | LWKEY_KP0, 85 | LWKEY_KP_DECIMAL, 86 | LWKEY_F11 = 87, 87 | LWKEY_F12, 88 | LWKEY_F13 = 100, 89 | LWKEY_F14, 90 | LWKEY_F15, 91 | LWKEY_F16, 92 | LWKEY_F17, 93 | LWKEY_F18, 94 | LWKEY_KP_EQU = 141, 95 | LWKEY_KP_ENTER = 156, 96 | LWKEY_RCTRL, 97 | LWKEY_KP_DIVIDE = 181, 98 | LWKEY_RALT = 184, 99 | LWKEY_PAUSE = 197, 100 | LWKEY_HOME = 199, 101 | LWKEY_UP, 102 | LWKEY_PAGEUP, 103 | LWKEY_LEFT = 203, 104 | LWKEY_RIGHT = 205, 105 | LWKEY_END = 207, 106 | LWKEY_DOWN, 107 | LWKEY_PAGEDOWN, 108 | LWKEY_INSERT, 109 | LWKEY_DELETE, 110 | LWKEY_LWIN = 219, 111 | LWKEY_RWIN 112 | } ELWJGLKey; 113 | 114 | typedef enum _ELWJGLMod { 115 | LWMOD_NONE = 0, 116 | LWMOD_CTRL = 1, 117 | LWMOD_SHIFT = 2, 118 | LWMOD_ALT = 4 119 | } ELWJGLMod; 120 | #endif 121 | -------------------------------------------------------------------------------- /src/str.h: -------------------------------------------------------------------------------- 1 | #ifndef STR_H 2 | #define STR_H 3 | #include "core.h" 4 | #include 5 | 6 | #define String_HexToInt(S) String_StrToLong(S, NULL, 16) 7 | #define String_AppendToArray(dst, from) String_Append(dst, sizeof(dst), from) 8 | #define String_CopyToArray(dst, from) String_Copy(dst, sizeof(dst), from) 9 | #define String_FormatErrorToArray(code, args) String_FormatError(code, buf, sizeof(buf), args) 10 | #define String_FormatBufVarargToArray(buf, fmt, args) String_FormatBufVararg(buf, sizeof(buf), fmt, args) 11 | #define String_FormatBufToArray(buf, fmt, ...) String_FormatBuf(buf, sizeof(buf), fmt, ##__VA_ARGS__) 12 | #define String_GetArgumentToArray(args, buf, index) String_GetArgument(args, buf, sizeof(buf), index) 13 | 14 | API cs_char *String_FindSubstr(cs_str str, cs_str strsrch); 15 | API cs_str String_TrimExtension(cs_str str); 16 | API cs_bool String_Compare(cs_str str1, cs_str str2); 17 | API cs_bool String_CaselessCompare(cs_str str1, cs_str str2); 18 | API cs_bool String_CaselessCompare2(cs_str str1, cs_str str2, cs_size len); 19 | API cs_size String_Length(cs_str str); 20 | API cs_size String_Append(cs_char *dst, cs_size len, cs_str src); 21 | API cs_char *String_Grow(cs_char *src, cs_size add, cs_size *new); 22 | API cs_size String_Copy(cs_char *dst, cs_size len, cs_str src); 23 | API cs_uint32 String_FormatError(cs_uint32 code, cs_char *buf, cs_size buflen, va_list *args); 24 | API cs_int32 String_FormatBufVararg(cs_char *buf, cs_size len, cs_str str, va_list *args); 25 | API cs_int32 String_FormatBuf(cs_char *buf, cs_size len, cs_str str, ...); 26 | API cs_char *String_LastChar(cs_str str, cs_char sym); 27 | API cs_char *String_FirstChar(cs_str str, cs_char sym); 28 | API cs_str String_AllocCopy(cs_str str); 29 | API cs_size String_GetArgument(cs_str args, cs_char *arg, cs_size len, cs_int32 index); 30 | API cs_uint32 String_CountArguments(cs_str args); 31 | API cs_bool String_IsSafe(cs_str str); 32 | API cs_str String_FromArgument(cs_str args, cs_int32 index); 33 | API cs_int32 String_ToInt(cs_str str); 34 | API cs_long String_StrToLong(cs_str str, cs_char **strend, cs_int32 radix); 35 | API cs_float String_ToFloat(cs_str str); 36 | API cs_size String_SizeOfB64(cs_size inlen); 37 | API cs_size String_ToB64(const cs_byte *src, cs_size len, cs_char *dst); 38 | #endif // STR_H 39 | -------------------------------------------------------------------------------- /src/vector.h: -------------------------------------------------------------------------------- 1 | #ifndef VECTOR_H 2 | #define VECTOR_H 3 | #include "core.h" 4 | #include "csmath.h" 5 | 6 | typedef struct _SVec { 7 | cs_int16 x, y, z; 8 | } SVec; 9 | 10 | typedef struct _Vec { 11 | cs_float x, y, z; 12 | } Vec; 13 | 14 | typedef struct _Ang { 15 | cs_float yaw, pitch; 16 | } Ang; 17 | 18 | static INL cs_bool SVec_Compare(const SVec *a, const SVec *b) { 19 | return a->x == b->x && a->y == b->y && a->z == b->z; 20 | } 21 | 22 | static INL cs_bool Vec_Compare(const Vec *a, const Vec *b) { 23 | return a->x == b->x && a->y == b->y && a->z == b->z; 24 | } 25 | 26 | static INL cs_bool Ang_Compare(const Ang *a, const Ang *b) { 27 | return a->yaw == b->yaw && a->pitch == b->pitch; 28 | } 29 | 30 | #define Ang_Set(a, ay, ap) (a).yaw = ay, (a).pitch = ap 31 | #define Vec_Add(d, v1, v2) (d).x = (v1).x + (v2).x, (d).y = (v1).y + (v2).y, (d).z = (v1).z + (v2).z 32 | #define Vec_Sub(d, v1, v2) (d).x = (v1).x - (v2).x, (d).y = (v1).y - (v2).y, (d).z = (v1).z - (v2).z 33 | #define Vec_Mul(d, v1, v2) (d).x = (v1).x * (v2).x, (d).y = (v1).y * (v2).y, (d).z = (v1).z * (v2).z 34 | #define Vec_Div(d, v1, v2) (d).x = (v1).x / (v2).x, (d).y = (v1).y / (v2).y, (d).z = (v1).z / (v2).z 35 | #define Vec_Min(d, v1, v2) (d).x = min((v1).x, (v2).x), (d).y = min((v1).y, (v2).y), (d).z = min((v1).z, (v2).z) 36 | #define Vec_Max(d, v1, v2) (d).x = max((v1).x, (v2).x), (d).y = max((v1).y, (v2).y), (d).z = max((v1).z, (v2).z) 37 | #define Vec_Cross(d, v1, v2) (d).x = (v1).y * (v2).z - (v1).z * (v2).y, (d).y = (v1).z * (v2).x - (v1).x * (v2).z, \ 38 | (d).z = (v1).x * (v2).y - (v1).y * (v2).x 39 | #define Vec_DivN(v, n) (v).x /= n, (v).y /= n, (v).z /= n 40 | #define Vec_Copy(dv, sv) (dv).x = (cs_float)(sv).x, (dv).y = (cs_float)(sv).y, (dv).z = (cs_float)(sv).z 41 | #define SVec_Copy(dv, sv) (dv).x = (cs_int16)(sv).x, (dv).y = (cs_int16)(sv).y, (dv).z = (cs_int16)(sv).z 42 | #define Vec_Set(v, vx, vy, vz) (v).x = vx, (v).y = vy, (v).z = vz 43 | #define Vec_IsNegative(v) ((v).x < 0 || (v).y < 0 || (v).z < 0) 44 | #define Vec_IsZero(v) ((v).x == 0 && (v).y == 0 && (v).z == 0) 45 | #define Vec_HaveZero(v) ((v).x == 0 || (v).y == 0 || (v).z == 0) 46 | #define Vec_Scale(v, s) (v).x *= s, (v).y *= s, (v).z *= s 47 | #define Vec_Distance(a, b) Math_Sqrt(Math_Sq((a).x - (b).x) + Math_Sq((a).y - (b).y) + Math_Sq((a).z - (b).z)) 48 | #endif // VECTOR_H 49 | -------------------------------------------------------------------------------- /src/http.h: -------------------------------------------------------------------------------- 1 | #ifndef HTTP_H 2 | #define HTTP_H 3 | #include "core.h" 4 | 5 | #define HTTP_USERAGENT SOFTWARE_FULLNAME 6 | 7 | #if defined(HTTP_USE_WININET_BACKEND) 8 | // Start of 9 | # define INTERNET_SERVICE_HTTP 3 10 | # define INTERNET_OPEN_TYPE_PRECONFIG 0 11 | # define INTERNET_FLAG_SECURE 0x00800000 12 | typedef void *HINTERNET; 13 | typedef cs_uint16 INTERNET_PORT; 14 | // End of 15 | 16 | typedef struct _Http { 17 | cs_bool secure; 18 | HINTERNET conn, req; 19 | } Http; 20 | #elif defined(HTTP_USE_CURL_BACKEND) 21 | # define CURLOPT_WRITEDATA 10000 + 1 22 | # define CURLOPT_URL 10000 + 2 23 | # define CURLOPT_USERAGENT 10000 + 18 24 | # define CURLOPT_FOLLOWLOCATION 0 + 52 25 | # define CURLOPT_WRITEFUNCTION 20000 + 11 26 | # define CURLE_OK 0 27 | 28 | typedef void CURL; 29 | typedef struct _Http { 30 | cs_bool secure; 31 | cs_str domain; 32 | cs_size buflen, rsplen; 33 | cs_char *path, *buf; 34 | CURL *handle; 35 | } Http; 36 | #else 37 | # error No HTTP backend selected 38 | #endif 39 | 40 | #ifndef CORE_BUILD_PLUGIN 41 | void Http_Uninit(void); 42 | #endif 43 | 44 | /** 45 | * @brief Устанавливает соединение с указанным сервером 46 | * по HTTP протоколу. 47 | * 48 | * @param http контекст http 49 | * @param domain домен сервера 50 | * @return true - подключение успешно, false - произошла внутренняя ошибка 51 | */ 52 | API cs_bool Http_Open(Http *http, cs_str domain); 53 | 54 | /** 55 | * @brief Выполняет запрос к серверу с указанным URL. 56 | * 57 | * @param http контекст http 58 | * @param url URL запроса 59 | * @return true - запрос успешен, false - ошибка при выполнении запроса 60 | */ 61 | API cs_bool Http_Request(Http *http, cs_str url); 62 | 63 | /** 64 | * @brief Читает ответ сервера в указанный буфер. 65 | * На данный момент, если буфера не хватает для чтения 66 | * ответа от сервера, то данные, на которые не хватило 67 | * места ПОТЕРЯЮТСЯ. 68 | * 69 | * @param http контекст http 70 | * @param buf буфер для ответа 71 | * @param sz размер буфера для ответа 72 | * @return количество прочтённых байт 73 | */ 74 | API cs_ulong Http_ReadResponse(Http *http, cs_char *buf, cs_ulong sz); 75 | 76 | /** 77 | * @brief Возвращает контекст в неинициализированное состояние, 78 | * в котором он находился до вызова Http_Open. 79 | * 80 | * @param http контекст http 81 | */ 82 | API void Http_Cleanup(Http *http); 83 | #endif // HTTP_H 84 | -------------------------------------------------------------------------------- /src/cserror.c: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | #ifdef CORE_USE_UNIX 3 | #define _GNU_SOURCE 4 | #endif 5 | #include "str.h" 6 | #include "cserror.h" 7 | #include "platform.h" 8 | 9 | #if defined(CORE_USE_WINDOWS) 10 | #pragma warning(push) 11 | #pragma warning(disable : 4255) 12 | #include 13 | #pragma warning(pop) 14 | 15 | NOINL static void PrintCallStack(void) { 16 | void *stack[16]; 17 | char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME]; 18 | 19 | PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer; 20 | symbol->MaxNameLen = MAX_SYM_NAME; 21 | symbol->SizeOfStruct = sizeof(SYMBOL_INFO); 22 | 23 | cs_uint16 frames = CaptureStackBackTrace(2, 16, stack, NULL); 24 | IMAGEHLP_LINE line = { 25 | .SizeOfStruct = sizeof(IMAGEHLP_LINE) 26 | }; 27 | 28 | for(cs_int32 i = 0; i < frames; i++) { 29 | if(SymFromAddr(GetCurrentProcess(), (cs_uintptr)stack[i], NULL, symbol)) { 30 | printf("Frame #%d: %s = %p\n", i, symbol->Name, (void *)symbol->Address); 31 | if(SymGetLineFromAddr(GetCurrentProcess(), (cs_uintptr)symbol->Address, (void *)&stack[i], &line)) 32 | printf("\tin %s:%ld\n", line.FileName, line.LineNumber); 33 | if(String_Compare(symbol->Name, "main")) break; 34 | } 35 | } 36 | } 37 | 38 | cs_bool Error_Init(void) { 39 | return SymInitialize(GetCurrentProcess(), NULL, true) != false; 40 | } 41 | 42 | void Error_Uninit(void) { 43 | SymCleanup(GetCurrentProcess()); 44 | } 45 | #elif defined(CORE_USE_UNIX) 46 | #include 47 | #if defined(CORE_USE_LINUX) || defined(CORE_USE_DARWIN) 48 | # include 49 | #endif 50 | 51 | static void PrintCallStack(void) { 52 | # if defined(CORE_USE_LINUX) || defined(CORE_USE_DARWIN) 53 | void *stack[16]; 54 | cs_int32 frames = backtrace(stack, 16); 55 | 56 | for(cs_int32 i = 0; i < frames; i++) { 57 | Dl_info dli; 58 | if(dladdr(stack[i], &dli)) { 59 | printf("Frame #%d: %s = %p\n", i, dli.dli_sname, dli.dli_saddr); 60 | if(String_Compare(dli.dli_sname, "main")) break; 61 | } 62 | } 63 | # else 64 | printf("*** Callstack printing is not implemented yet for this OS\n"); 65 | # endif 66 | } 67 | 68 | cs_bool Error_Init(void) {return true;} 69 | void Error_Uninit(void) {} 70 | #endif 71 | 72 | void Error_Print(cs_bool abort, cs_int32 code, cs_str file, cs_uint32 line, cs_str func, ...) { 73 | cs_char strbuf[384]; 74 | 75 | cs_int32 fmtpos = String_FormatBuf(strbuf, 384, "%s:%d in function %s: ", file, line, func); 76 | va_list args; 77 | va_start(args, func); 78 | String_FormatError(code, strbuf + fmtpos, 384 - fmtpos, &args); 79 | printf("%s\n", strbuf); 80 | va_end(args); 81 | PrintCallStack(); 82 | 83 | if(abort) Process_Exit(code); 84 | } 85 | -------------------------------------------------------------------------------- /src/event.c: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | #include "event.h" 3 | 4 | typedef struct { 5 | cs_uint32 rtype; 6 | union _UCallbacks { 7 | evtBoolCallback fbool; 8 | evtVoidCallback fvoid; 9 | void *fptr; 10 | } func; 11 | } Event; 12 | 13 | #define EVENTS_FCOUNT 128 14 | 15 | static Event regEvents[EVENTS_TCOUNT][EVENTS_FCOUNT] = {{{0, {NULL}}}}; 16 | 17 | #define rgPart1 \ 18 | for(cs_int32 pos = 0; pos < EVENTS_FCOUNT; pos++) { \ 19 | Event *evt = ®Events[type][pos]; \ 20 | if(evt->func.fptr) continue; 21 | 22 | #define rgPart2 \ 23 | return true; \ 24 | } \ 25 | return false; 26 | 27 | cs_bool Event_RegisterVoid(EventType type, evtVoidCallback func) { 28 | rgPart1 29 | evt->rtype = 0; 30 | evt->func.fvoid = func; 31 | rgPart2 32 | } 33 | 34 | cs_bool Event_RegisterBool(EventType type, evtBoolCallback func) { 35 | rgPart1 36 | evt->rtype = 1; 37 | evt->func.fbool = func; 38 | rgPart2 39 | } 40 | 41 | cs_bool Event_RegisterBunch(EventRegBunch *bunch) { 42 | for(cs_int32 i = 0; bunch[i].evtfunc; i++) { 43 | switch(bunch[i].ret) { 44 | case 'b': 45 | if(!Event_RegisterBool(bunch[i].type, (evtBoolCallback)bunch[i].evtfunc)) 46 | return false; 47 | break; 48 | case 'v': 49 | if(!Event_RegisterVoid(bunch[i].type, (evtVoidCallback)bunch[i].evtfunc)) 50 | return false; 51 | break; 52 | default: 53 | return false; 54 | } 55 | } 56 | 57 | return true; 58 | } 59 | 60 | cs_bool Event_Unregister(EventType type, void *evtFuncPtr) { 61 | for(cs_int32 pos = 0; pos < EVENTS_FCOUNT; pos++) { 62 | Event *evt = ®Events[type][pos]; 63 | 64 | if(evt && evt->func.fptr == evtFuncPtr) { 65 | regEvents[type][pos].func.fptr = NULL; 66 | return true; 67 | } 68 | } 69 | return false; 70 | } 71 | 72 | void Event_UnregisterBunch(EventRegBunch *bunch) { 73 | for(cs_int32 i = 0; bunch[i].evtfunc; i++) 74 | Event_Unregister(bunch[i].type, bunch[i].evtfunc); 75 | } 76 | 77 | void Event_UnregisterAll(void) { 78 | for(cs_int32 type = 0; type < EVENTS_TCOUNT; type++) { 79 | for(cs_int32 pos = 0; pos < EVENTS_FCOUNT; pos++) { 80 | regEvents[type][pos].func.fptr = NULL; 81 | } 82 | } 83 | } 84 | 85 | cs_bool Event_Call(EventType type, void *param) { 86 | if(type >= EVENTS_TCOUNT) return false; 87 | cs_bool ret = true; 88 | 89 | for(cs_int32 pos = 0; pos < EVENTS_FCOUNT; pos++) { 90 | Event *evt = ®Events[type][pos]; 91 | if(!evt->func.fptr) continue; 92 | 93 | if(evt->rtype == 1) 94 | ret = evt->func.fbool(param); 95 | else 96 | evt->func.fvoid(param); 97 | 98 | if(!ret) break; 99 | } 100 | 101 | return ret; 102 | } 103 | -------------------------------------------------------------------------------- /src/assoc.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file assoc.h 3 | * @author igor725 (gvaldovigor@gmail.com) 4 | * @brief Данный модуль может быть использован плагином сервера 5 | * для присваивания любому клиенту (либо миру) участка памяти с 6 | * определённым типом. К одному объекту может быть привязан 7 | * только один участок памяти одного типа. 8 | * 9 | * Примеры применения ассоциаций можно найти тут: 10 | * https://github.com/igor725/cs-survival 11 | * https://github.com/igor725/cs-worldedit 12 | * 13 | * @copyright Copyright (c) 2022 14 | * 15 | */ 16 | 17 | #ifndef ASSOC_H 18 | #define ASSOC_H 19 | #include "core.h" 20 | #include "types/assoc.h" 21 | 22 | /** 23 | * @brief Создаёт новый ассоциативный тип. 24 | * 25 | * @param bindto вид объектов, к которым будет цепляться данный тип 26 | * @return уникальный номер созданного ассоциативного типа, либо -1 27 | * в случае, если уже было создано максимально допустимое количество 28 | * ассоциативных типов. 29 | */ 30 | API AssocType Assoc_NewType(EAssocBindType bindto); 31 | 32 | /** 33 | * @brief Удаляет созданный ранее ассоциативный тип. 34 | * После удаления типа ВСЕ связанные с ним участки памяти 35 | * будут также удалены. Убедитесь, что эта память нигде не 36 | * используется. 37 | * 38 | * @param type номер типа, полученный из вызова Assoc_NewType() 39 | * @return true - тип был удалён успешно, false - произошла ошибка 40 | */ 41 | API cs_bool Assoc_DelType(AssocType type); 42 | 43 | /** 44 | * @brief Выделение участка памяти для указанного объекта. 45 | * 46 | * Если target не будет принадлежать к объектам, для которых был 47 | * создан ассоциативный тип, то вызов этой функция может привести 48 | * к падению сервера. 49 | * 50 | * @param target указатель на объект, к которому будет привязана выделенная память 51 | * @param type ассоциативный тип 52 | * @param num количество выделяемых блоков 53 | * @param size размер выделяемого блока (в байтах) 54 | * @return указатель на выделенную память 55 | */ 56 | API AssocMem Assoc_AllocFor(void *target, AssocType type, cs_size num, cs_size size); 57 | 58 | /** 59 | * @brief Ищет выделенную ранее память для указанного объекта. 60 | * 61 | * @param target указатель на объект, к которому была привязана искомая память 62 | * @param type ассоциативный тип 63 | * @return указатель на выделенную память, либо NULL, если она не выделялась 64 | */ 65 | API AssocMem Assoc_GetPtr(void *target, AssocType type); 66 | 67 | /** 68 | * @brief Высвобождает выделенную ранее память для указанного объекта. 69 | * 70 | * @param target указатель на объект, к которому была привязана искомая память 71 | * @param type ассоциативный тип 72 | * @return true - память высвобождена, false - в объекте не 73 | * найдена память указанного ассоциативного типа 74 | */ 75 | API cs_bool Assoc_Remove(void *target, AssocType type); 76 | #endif 77 | -------------------------------------------------------------------------------- /src/types/event.h: -------------------------------------------------------------------------------- 1 | #ifndef EVENTTYPES_H 2 | #define EVENTTYPES_H 3 | #include "core.h" 4 | #include "vector.h" 5 | #include "types/block.h" 6 | #include "types/client.h" 7 | #include "types/world.h" 8 | #include "types/command.h" 9 | 10 | typedef void(*evtVoidCallback)(void *); 11 | typedef cs_bool(*evtBoolCallback)(void *); 12 | 13 | typedef enum _EventType { 14 | EVT_POSTSTART, 15 | 16 | EVT_ONTICK, 17 | EVT_ONSTOP, 18 | EVT_ONCONNECT, 19 | EVT_ONHANDSHAKEDONE, 20 | EVT_ONUSERTYPECHANGE, 21 | EVT_ONDISCONNECT, 22 | EVT_ONSPAWN, 23 | EVT_ONDESPAWN, 24 | EVT_ONMESSAGE, 25 | EVT_ONHELDBLOCKCHNG, 26 | EVT_ONBLOCKPLACE, 27 | EVT_ONPING, 28 | EVT_ONCLICK, 29 | EVT_ONMOVE, 30 | EVT_ONROTATE, 31 | EVT_ONWORLDSTATUSCHANGE, 32 | EVT_ONWORLDADDED, 33 | EVT_ONWORLDREMOVED, 34 | EVT_ONPLUGINLOAD, 35 | EVT_ONPLUGINUNLOAD, 36 | EVT_ONPLUGINMESSAGE, 37 | EVT_ONLOG, 38 | 39 | EVT_PRECOMMAND, 40 | EVT_PREHANDSHAKEDONE, 41 | EVT_PREWORLDENVUPDATE, 42 | 43 | EVENTS_TCOUNT 44 | } EventType; 45 | 46 | typedef struct _EventRegBunch { 47 | cs_byte ret; 48 | EventType type; 49 | void *evtfunc; 50 | } EventRegBunch; 51 | 52 | typedef struct _onHandshakeDone { 53 | Client *const client; 54 | World *world; 55 | } onHandshakeDone; 56 | 57 | typedef struct _onSpawn { 58 | Client *const client; 59 | Vec *const position; 60 | Ang *const angle; 61 | cs_bool updateenv; 62 | } onSpawn; 63 | 64 | typedef struct _onMessage { 65 | Client *const client; 66 | cs_char message[CPE_MAX_EXTMESG_LEN]; 67 | cs_byte type; 68 | } onMessage; 69 | 70 | typedef struct _onHeldBlockChange { 71 | Client *const client; 72 | const BlockID prev, curr; 73 | } onHeldBlockChange; 74 | 75 | /** 76 | * TODO: 77 | * Расставить для двух следующих 78 | * эвентов const спецификаторы, 79 | * когда предоставится возможность. 80 | */ 81 | typedef struct _onBlockPlace { 82 | Client *client; 83 | ESetBlockMode mode; 84 | SVec pos; 85 | BlockID id; 86 | } onBlockPlace; 87 | 88 | typedef struct _onPlayerClick { 89 | Client *client; 90 | cs_int8 button, action; 91 | Ang angle; 92 | ClientID tgid; 93 | SVec tgpos; 94 | EBlockFace tgface; 95 | } onPlayerClick; 96 | 97 | typedef struct _onPluginMessage { 98 | Client *const client; 99 | const cs_byte channel; 100 | cs_char message[MAX_STR_LEN]; 101 | } onPluginMessage; 102 | 103 | typedef struct _preCommand { 104 | Command *const command; 105 | Client *const caller; 106 | cs_str const args; 107 | cs_bool allowed; 108 | } preCommand; 109 | 110 | typedef struct _preHandshakeDone { 111 | Client *const client; 112 | cs_char name[MAX_STR_LEN], motd[MAX_STR_LEN]; 113 | } preHandshakeDone; 114 | 115 | typedef struct _preWorldEnvUpdate { 116 | World *const world; 117 | const cs_byte values; 118 | const cs_uint16 props; 119 | const cs_byte colors; 120 | } preWorldEnvUpdate; 121 | #endif 122 | -------------------------------------------------------------------------------- /src/types/client.h: -------------------------------------------------------------------------------- 1 | #ifndef CLIENTTYPES_H 2 | #define CLIENTTYPES_H 3 | #include "core.h" 4 | #include "vector.h" 5 | #include "types/cpe.h" 6 | #include "types/list.h" 7 | #include "types/world.h" 8 | #include "types/compr.h" 9 | #include "types/websock.h" 10 | #include "types/netbuffer.h" 11 | #include "types/protocol.h" 12 | 13 | #define CLIENT_SELF (ClientID)-1 14 | #define CLIENT_BROADCAST (Client *)NULL 15 | 16 | typedef enum _EMesgType { 17 | MESSAGE_TYPE_CHAT, // Сообщение в чате 18 | MESSAGE_TYPE_STATUS1, // Правый верхний угол 19 | MESSAGE_TYPE_STATUS2, 20 | MESSAGE_TYPE_STATUS3, 21 | MESSAGE_TYPE_BRIGHT1 = 11, // Правый нижний угол 22 | MESSAGE_TYPE_BRIGHT2, 23 | MESSAGE_TYPE_BRIGHT3, 24 | MESSAGE_TYPE_ANNOUNCE = 100, // Сообщение в середине экрана 25 | MESSAGE_TYPE_BIGANNOUNCE, 26 | MESSAGE_TYPE_SMALLANNOUNCE 27 | } EMesgType; 28 | 29 | typedef enum _EClientState { 30 | CLIENT_STATE_INITIAL, // Игрок только подключился 31 | CLIENT_STATE_MOTD, // Игрок получает карту 32 | CLIENT_STATE_INGAME // Игрок находится в игре 33 | } EClientState; 34 | 35 | typedef struct _PlayerData { 36 | cs_char key[MAX_STR_LEN]; // Ключ, полученный от игрока 37 | cs_char name[MAX_STR_LEN]; // Имя игрока 38 | cs_char displayname[MAX_STR_LEN]; // Отображаемое имя игрока 39 | World *world; // Мир, в котором игрок обитает 40 | Vec position; // Позиция игрока 41 | Ang angle; // Угол вращения игрока 42 | cs_bool isOP; // Является ли игрок оператором 43 | cs_bool spawned; // Заспавнен ли игрок 44 | cs_bool firstSpawn; // Был лы этот спавн первым с момента захода на сервер 45 | } PlayerData; 46 | 47 | typedef struct _PacketData { 48 | Packet *packet; 49 | cs_uint16 psize; 50 | cs_bool isExtended; 51 | } PacketData; 52 | 53 | typedef struct _MapData { 54 | Compr compr; // Штука для сжатия карты 55 | World *world; // Передаваемая карта 56 | BlockID *cbptr; // Указатель на место, с которого отправляем карту 57 | cs_uint32 size; // Размер карты в байтах 58 | cs_uint32 sent; // Количество отправленных байт 59 | cs_bool fback; // Нужно ли заменять кастомные блоки 60 | cs_bool fastmap; // Есть ли дополнение FastMap 61 | } MapData; 62 | 63 | typedef struct _Client { 64 | ClientID id; // Используется в качестве entityid 65 | EClientState state; // Текущее состояние игрока 66 | cs_str kickReason; // Причина кика, если имеется 67 | cs_uint64 lastmsg; // Временная метка последнего полученного от клиента сообщения 68 | cs_ulong addr; // ipv4 адрес клиента 69 | NetBuffer netbuf; // Прикол для обмена данными 70 | PacketData packetData; // Стейт получения пакета от клиента 71 | MapData mapData; // Стейт отправки карты игроку 72 | CPEData cpeData; // CPE-информация игрока 73 | PlayerData playerData; // Информация о игроке 74 | KListField *headNode; // Последняя созданная ассоциативная нода у клиента 75 | WebSock *websock; // Создаётся, если клиент был определён как браузерный 76 | Mutex *mutex; // Мьютекс записи, на время отправки пакета по сокету он лочится 77 | } Client; 78 | #endif 79 | -------------------------------------------------------------------------------- /src/world.h: -------------------------------------------------------------------------------- 1 | #ifndef WORLD_H 2 | #define WORLD_H 3 | #include "core.h" 4 | #include "vector.h" 5 | #include "types/list.h" 6 | #include "types/world.h" 7 | #include "types/cpe.h" 8 | 9 | #ifdef CORE_USE_LITTLE 10 | # define WORLD_MAGIC 0x54414457u 11 | #else 12 | # define WORLD_MAGIC 0x57444154u 13 | #endif 14 | 15 | API cs_bool World_HasError(World *world); 16 | API EWorldError World_PopError(World *world, EWorldExtra *extra); 17 | 18 | API World *World_Create(cs_str name); 19 | API void World_AllocBlockArray(World *world); 20 | API cs_bool World_CleanBlockArray(World *world); 21 | API void World_FreeBlockArray(World *world); 22 | API void World_Free(World *world); 23 | API void World_Add(World *world); 24 | API cs_bool World_Remove(World *world); 25 | API cs_bool World_IsReadyToPlay(World *world); 26 | API cs_bool World_IsInMemory(World *world); 27 | API cs_bool World_IsModified(World *world); 28 | API cs_bool World_FinishEnvUpdate(World *world); 29 | API cs_byte World_CountPlayers(World *world); 30 | 31 | API cs_bool World_Load(World *world); 32 | API void World_Unload(World *world); 33 | API cs_bool World_Save(World *world); 34 | 35 | API cs_bool World_Lock(World *world, cs_ulong timeout); 36 | API void World_Unlock(World *world); 37 | API void World_StartTask(World *world); 38 | API void World_EndTask(World *world); 39 | API void World_WaitAllTasks(World *world); 40 | 41 | API void World_SetInMemory(World *world, cs_bool state); 42 | API void World_SetIgnoreModifications(World *world, cs_bool state); 43 | API void World_SetSpawn(World *world, Vec *svec, Ang *sang); 44 | API cs_bool World_SetDimensions(World *world, const SVec *dims); 45 | API cs_bool World_SetBlock(World *world, SVec *pos, BlockID id); 46 | API cs_bool World_SetBlockO(World *world, cs_uint32 offset, BlockID id); 47 | API cs_bool World_SetEnvColor(World *world, EColor type, Color3* color); 48 | API cs_bool World_SetEnvProp(World *world, EProp prop, cs_int32 value); 49 | API cs_bool World_SetTexturePack(World *world, cs_str url); 50 | API cs_bool World_SetWeather(World *world, EWeather type); 51 | API void World_SetSeed(World *world, cs_uint32 seed); 52 | 53 | API cs_str World_GetName(World *world); 54 | API void World_GetSpawn(World *world, Vec *svec, Ang *sang); 55 | API void *World_GetData(World *world, cs_uint32 *size); 56 | API BlockID *World_GetBlockArray(World *world, cs_uint32 *size); 57 | API cs_uint32 World_GetBlockArraySize(World *world); 58 | API cs_uint32 World_GetOffset(World *world, SVec *pos); 59 | API void World_GetDimensions(World *world, SVec *dims); 60 | API BlockID World_GetBlock(World *world, SVec *pos); 61 | API BlockID World_GetBlockO(World *world, cs_uint32 offset); 62 | API cs_int32 World_GetEnvProp(World *world, EProp prop); 63 | API cs_bool World_GetEnvColor(World *world, EColor type, Color3 *dst); 64 | API EWeather World_GetWeather(World *world); 65 | API cs_str World_GetTexturePack(World *world); 66 | API cs_uint32 World_GetSeed(World *world); 67 | 68 | API World *World_GetByName(cs_str name); 69 | 70 | VAR World *World_Main; 71 | VAR AListField *World_Head; 72 | #endif // WORLD_H 73 | -------------------------------------------------------------------------------- /misc/zdetect.bat: -------------------------------------------------------------------------------- 1 | :detectzlib 2 | SET ZFOLDER=..\zlib 3 | IF EXIST ".\zlib.path" ( 4 | FOR /F "delims=" %%F IN (.\zlib.path) DO ( 5 | IF NOT "%%F"=="" SET ZFOLDER=%%F 6 | ) 7 | ) ELSE IF !NOPROMPT! EQU 0 ( 8 | ECHO Type absolute or relative path ^(without quotes^) to the folder that containing zlib repo. 9 | ECHO If specified folder is empty or does not contain zlib source code, it will be cloned and 10 | ECHO builded by this script automatically. 11 | ECHO Hint: If you leave the path field empty, the script will use "!ZFOLDER!" by default. 12 | :zfolderloop 13 | SET /P UZF="zlib path> " 14 | IF NOT "!UZF!"=="" ( 15 | IF NOT EXIST "!UZF!" MD "!UZF!" 16 | SET ZFOLDER=!UZF! 17 | ) 18 | ECHO !ZFOLDER!>.\zlib.path 19 | ) 20 | 21 | IF NOT EXIST "!ZFOLDER!\" GOTO nozlib 22 | IF NOT EXIST "!ZFOLDER!\zlib.h" GOTO nozlib 23 | IF NOT EXIST "!ZFOLDER!\zconf.h" GOTO nozlib 24 | SET INCLUDE=!INCLUDE!;!ZFOLDER!\ 25 | FOR /F "tokens=* USEBACKQ" %%F IN (`WHERE /R "!ZFOLDER!\win32\!ARCH!" z*.dll`) DO ( 26 | @REM Добавить проверку найденной DLLки на совместимость с текущей системой 27 | SET ZLIB_DYNAMIC=%%F 28 | ) 29 | IF "!ZLIB_DYNAMIC!"=="" ( 30 | GOTO makezlib_st1 31 | ) ELSE ( 32 | IF NOT EXIST "!ZLIB_DYNAMIC!" ( 33 | GOTO makezlib_st1 34 | ) ELSE ( 35 | GOTO copyzlib 36 | ) 37 | ) 38 | 39 | :makezlib_st1 40 | IF NOT EXIST "!ZFOLDER!\win32\Makefile.msc" ( 41 | GOTO nozlib 42 | ) ELSE ( 43 | GOTO makezlib_st2 44 | ) 45 | 46 | :nozlib 47 | IF !NOPROMPT! EQU 0 ( 48 | ECHO zlib not found in "!ZFOLDER!". 49 | ECHO Would you like the script to automatically clone and build zlib library? 50 | ECHO Note: The zlib repo will be cloned from Mark Adler's GitHub, then compiled. 51 | ECHO Warning: If "!ZFOLDER!" exists it will be removed! 52 | SET /P ZQUESTION="[Y/n]>" 53 | IF "!ZQUESTION!"=="" GOTO downzlib 54 | IF "!ZQUESTION!"=="y" GOTO downzlib 55 | IF "!ZQUESTION!"=="Y" GOTO downzlib 56 | GOTO zclonefail 57 | ) 58 | 59 | :downzlib 60 | IF !GITOK! EQU 0 ( 61 | ECHO Looks like you don't have Git for Windows installed 62 | ECHO You can download it from https://git-scm.com/download/win 63 | ) ELSE ( 64 | RMDIR /S /Q "!ZFOLDER!" 65 | git clone https://github.com/madler/zlib "!ZFOLDER!" 66 | GOTO makezlib_st2 67 | ) 68 | 69 | :makezlib_st2 70 | IF NOT EXIST "!ZFOLDER!\win32\!ARCH!" MD "!ZFOLDER!\win32\!ARCH!" 71 | PUSHD "!ZFOLDER!\win32\!ARCH!" 72 | NMAKE /F ..\Makefile.msc TOP=..\..\ 73 | IF !ERRORLEVEL! EQU 0 ( 74 | POPD 75 | GOTO detectzlib 76 | ) else ( 77 | POPD 78 | GOTO zcompilefail 79 | ) 80 | 81 | :copyzlib 82 | FOR /F "tokens=* USEBACKQ" %%F IN (`WHERE /R !SERVER_OUTROOT! zlib1.dll`) DO ( 83 | IF EXIST "%%F" GOTO zlibok 84 | ) 85 | COPY "!ZLIB_DYNAMIC!" "!SERVER_OUTROOT!\zlib1.dll" 86 | IF !DEBUG! EQU 1 ( 87 | COPY "!ZLIB_DYNAMIC:~0,-3!pdb" "!SERVER_OUTROOT!\zlib1.pdb" 88 | ) 89 | GOTO zlibok 90 | 91 | :zclonefail 92 | ECHO Error: Failed to clone zlib repo 93 | EXIT /B 1 94 | 95 | :zcompilefail 96 | ECHO Error: failed to compile zlib 97 | EXIT /B 1 98 | 99 | :zlibok 100 | EXIT /B 0 101 | -------------------------------------------------------------------------------- /src/block.h: -------------------------------------------------------------------------------- 1 | #ifndef BLOCK_H 2 | #define BLOCK_H 3 | #include "core.h" 4 | #include "types/world.h" 5 | #include "types/cpe.h" 6 | #include "types/block.h" 7 | 8 | #ifndef CORE_BUILD_PLUGIN 9 | BlockDef *Block_GetDefinition(World *world, BlockID id); 10 | #endif 11 | 12 | /** 13 | * @brief Возвращает фоллбек блок для указанного блока 14 | * на случай, если клиент не поддерживает дополнения 15 | * CPE. 16 | * 17 | * @param world целевой мир 18 | * @param id любой уникальный номер блока 19 | * @return уникальный номер vanilla-блока 20 | */ 21 | API BlockID Block_GetFallbackFor(World *world, BlockID id); 22 | 23 | /** 24 | * @brief Проверяет, существует ли блок под указанным номером. 25 | * 26 | * @param world целевой мир 27 | * @param id уникальный номер блока 28 | * @return true - блок существует, false - блок не существует 29 | */ 30 | API cs_bool Block_IsValid(World *world, BlockID id); 31 | 32 | /** 33 | * @brief Возвращает имя указанного блока. В случае, если блок 34 | * не существует вернёт строку "Unknown block". 35 | * 36 | * @param world целевой мир 37 | * @param id уникальный номер блока 38 | * @return строка с именем блока 39 | */ 40 | API cs_str Block_GetName(World *world, BlockID id); 41 | 42 | API cs_bool Block_IsDefinedFor(World *world, BlockDef *bdef); 43 | 44 | /** 45 | * @brief Очищает у блока флаги UPDATED и UNDEFINED и 46 | * добавляет его к массиву указанного мира. 47 | * 48 | * @param world целевой мир 49 | * @param bdef структура, описывающая блока 50 | * @return true - регистрация прошла успешно, false - блок с таким id уже зарегистрирован 51 | */ 52 | API cs_bool Block_Define(World *world, BlockID id, BlockDef *bdef); 53 | 54 | /** 55 | * @brief Удаляет указанный блок для одного мира 56 | * 57 | * @param world целевой мир 58 | * @param bdef структура, описывающая блок 59 | * @return true - блок удалён, false - блок не был зарегистрирован 60 | */ 61 | API cs_bool Block_Undefine(World *world, BlockDef *bdef); 62 | 63 | /** 64 | * @brief Устанавливает указанному блоку флаг UNDEFINED 65 | * 66 | * @param bdef структура, описывающая блок 67 | */ 68 | API void Block_UndefineGlobal(BlockDef *bdef); 69 | 70 | /** 71 | * @brief Рассылает всем клиентам миров, для которых 72 | * данным блок зарегистрирован обновления состояния 73 | * блока. 74 | * 75 | * @param bdef структура, описывающая блок 76 | */ 77 | API void Block_UpdateDefinition(BlockDef *bdef); 78 | 79 | /** 80 | * @brief Добавляет блок в кучу. 81 | * 82 | * @param bbu указатель на кучу 83 | * @param offset смещение блока в мире 84 | * @param id уникальный номер блока 85 | * @return true - блок добавлен, false - произошла ошибка 86 | */ 87 | API cs_bool Block_BulkUpdateAdd(BulkBlockUpdate *bbu, cs_uint32 offset, BlockID id); 88 | 89 | /** 90 | * @brief Рассылает всем игрокам указанного в куче мира обновлённые блоки. 91 | * 92 | * @param bbu указатель на кучу 93 | * @return true - пакет добавлен в очередь, false - отправка провалилась 94 | */ 95 | API cs_bool Block_BulkUpdateSend(BulkBlockUpdate *bbu); 96 | 97 | /** 98 | * @brief Очищает кучу от добавленных в неё блоков. 99 | * 100 | * @param bbu указатель на кучу 101 | */ 102 | API void Block_BulkUpdateClean(BulkBlockUpdate *bbu); 103 | #endif // BLOCK_H 104 | -------------------------------------------------------------------------------- /src/compr.h: -------------------------------------------------------------------------------- 1 | #ifndef COMPR_H 2 | #define COMPR_H 3 | #include "core.h" 4 | #include "types/compr.h" 5 | 6 | /** 7 | * @brief Инициализирует архиватор. 8 | * 9 | * @param ctx указатель на контекст архиватора 10 | * @param type тип архиватора 11 | * @return true - архиватор инициализорван, false - ошибка инициализации 12 | */ 13 | API cs_bool Compr_Init(Compr *ctx, ComprType type); 14 | 15 | /** 16 | * @brief Проверяет, находится ли архиватор в указанном состоянии. 17 | * 18 | * @param ctx указатель на контекст архиватора 19 | * @param state ожидаемое состояние 20 | * @return true - находится, false - нет 21 | */ 22 | API cs_bool Compr_IsInState(Compr *ctx, ComprState state); 23 | 24 | /** 25 | * @brief Возвращает CRC32 хеш для переданных данных. 26 | * 27 | * @param data указатель на данные 28 | * @param len размер данных в байтах 29 | * @return CRC32 хеш 30 | */ 31 | API cs_ulong Compr_CRC32(const cs_byte *data, cs_uint32 len); 32 | 33 | /** 34 | * @brief Устанавливает архиватору входной буфер. 35 | * 36 | * @param ctx указатель на контекст архиватора 37 | * @param data указатель на буфер 38 | * @param size размер буфера 39 | */ 40 | API void Compr_SetInBuffer(Compr *ctx, void *data, cs_uint32 size); 41 | 42 | /** 43 | * @brief Устанавливает архиватору выходной буфер. 44 | * 45 | * @param ctx указатель на контекст архиватора 46 | * @param data указатель на буфер 47 | * @param size размер буфера 48 | */ 49 | API void Compr_SetOutBuffer(Compr *ctx, void *data, cs_uint32 size); 50 | 51 | /** 52 | * @brief Выполняет один шаг архиватора 53 | * 54 | * @param ctx указатель на контекст архиватора 55 | * @return true - архиватор завершил работу, false - требуется ещё один шаг 56 | */ 57 | API cs_bool Compr_Update(Compr *ctx); 58 | 59 | /** 60 | * @brief Возвращает текст ошибки по её коду. 61 | * 62 | * @param code код ошибки 63 | * @return строка с ошибкой 64 | */ 65 | API cs_str Compr_GetError(cs_int32 code); 66 | 67 | /** 68 | * @brief Возвращает последнюю произошедшую в архиваторе ошибку. 69 | * 70 | * @param ctx указатель на контекст архиватора 71 | * @return строка с ошибкой 72 | */ 73 | API cs_str Compr_GetLastError(Compr *ctx); 74 | 75 | /** 76 | * @brief Возвращает количество (в байтах) данных, 77 | * которые в данный момент необходимо сжать. 78 | * 79 | * Обновляется ТОЛЬКО после полного шага архиватора! 80 | * 81 | * @param ctx указатель на контекст архиватора 82 | * @return количество запланированных на сжатие данных 83 | */ 84 | API cs_uint32 Compr_GetQueuedSize(Compr *ctx); 85 | 86 | /** 87 | * @brief Возвращает количество (в байтах) данных, 88 | * которые были записаны в выходной буфер с момента 89 | * последнего исполнения функции Compr_Update. 90 | * 91 | * @param ctx указатель на контекст архиватора 92 | * @return количество записанных данных 93 | */ 94 | API cs_uint32 Compr_GetWrittenSize(Compr *ctx); 95 | 96 | /** 97 | * @brief Возвращает архиватор в начальное состояние. 98 | * 99 | * @param ctx указатель на контекст архиватора 100 | */ 101 | API void Compr_Reset(Compr *ctx); 102 | 103 | /** 104 | * @brief Высвобождает память, выделенную под внутренний zlib контекст архиватора. 105 | * 106 | * @param ctx указатель на контекст архиватора 107 | */ 108 | API void Compr_Cleanup(Compr *ctx); 109 | 110 | #ifndef CORE_BUILD_PLUGIN 111 | /** 112 | * @brief Отключает библиотеку zlib. 113 | * (При следующем вызове Compr_Init произойдёт повторное подключение) 114 | * 115 | */ 116 | void Compr_Uninit(void); 117 | #endif 118 | 119 | #endif 120 | -------------------------------------------------------------------------------- /src/heartbeat.h: -------------------------------------------------------------------------------- 1 | #ifndef HEARTBEAT_H 2 | #define HEARTBEAT_H 3 | #include "core.h" 4 | #include "types/client.h" 5 | #include "types/platform.h" 6 | 7 | #define HEARTBEAT_SECRET_LENGTH 32 8 | 9 | typedef cs_bool(*heartbeatKeyChecker)(cs_str secret, Client *client); 10 | 11 | typedef struct _Heartbeat { 12 | heartbeatKeyChecker checker; 13 | cs_str domain, playurl, reqpath; 14 | cs_char secret[HEARTBEAT_SECRET_LENGTH]; 15 | Waitable *isdone; 16 | cs_bool started, keychanged, 17 | ispublic, issecure, isannounced; 18 | cs_uint16 delay; 19 | } Heartbeat; 20 | 21 | #ifndef CORE_BUILD_PLUGIN 22 | /** 23 | * @brief Производит проверку ключа игрока на действительность 24 | * всеми доступными функциями проверки ключа. Для игроков с 25 | * 127.0.0.1 всегда возвращает true. 26 | * 27 | * @param client указатель на Client 28 | * @return cs_bool верен ли ключ 29 | */ 30 | cs_bool Heartbeat_Validate(Client *client); 31 | void Heartbeat_StopAll(void); 32 | #endif 33 | 34 | /** 35 | * @brief Создаёт новый heartbeat. 36 | * 37 | * @return указатель на heartbeat 38 | */ 39 | API Heartbeat *Heartbeat_New(void); 40 | 41 | /** 42 | * @brief Устанавливает домен для heartbeat. 43 | * 44 | * @param self указатель на heartbeat 45 | * @param domain целевой сервер для отправки hearbeat запросов 46 | * @return true - домен установлен, false - heartbeat уже запущен 47 | */ 48 | API cs_bool Heartbeat_SetDomain(Heartbeat *self, cs_str domain); 49 | 50 | /** 51 | * @brief Устанавливает путь запроса. 52 | * Стоковый heartbeat игры имеет следующий путь: 53 | * /heartbeat.jsp 54 | * 55 | * @param self указатель на heartbeat 56 | * @param path путь запроса 57 | * @return true - путь установлен, false - heartbeat уже запущен 58 | */ 59 | API cs_bool Heartbeat_SetRequestPath(Heartbeat *self, cs_str path); 60 | 61 | /** 62 | * @brief Устанавливает строку, которая будет использована 63 | * при поиске игровой ссылки, в ответе от heartbeat сервера. 64 | * 65 | * @param self указатель на heartbeat 66 | * @param url кусок ссылки, который нужно найти в ответе от сервера 67 | * @return true - ссылка установлена, false - heartbeat уже запущен 68 | */ 69 | API cs_bool Heartbeat_SetPlayURL(Heartbeat *self, cs_str url); 70 | 71 | /** 72 | * @brief Устанавливает функцию для проверки ключа игрока. 73 | * Если функция не была задана до Heartbeat_Run, то будет 74 | * использована стандартная функция проверки ключа игрока. 75 | * 76 | * @param self указатель на heartbeat 77 | * @param func функция проверки ключа 78 | * @return true - функция установлена, false - heartbeat уже запущен 79 | */ 80 | API cs_bool Heartbeat_SetKeyChecker(Heartbeat *self, heartbeatKeyChecker func); 81 | 82 | /** 83 | * @brief Устанавливает видимость сервера в списке серверов. 84 | * 85 | * @param self указатель на hearbeat 86 | * @param state значение видимости 87 | */ 88 | API void Heartbeat_SetPublic(Heartbeat *self, cs_bool state); 89 | 90 | /** 91 | * @brief Задержка между запросами к heartbeat серверу. 92 | * 93 | * @param self указатель на heartbeat 94 | * @param delay задержка (мсек) 95 | */ 96 | API void Heartbeat_SetDelay(Heartbeat *self, cs_uint16 delay); 97 | 98 | /** 99 | * @brief Запускает поток, осуществляющий запросы к 100 | * heartbeat серверу. 101 | * 102 | * @param self указатель на heartbeat 103 | * @return true - запуск успешен, false - какой-то из параметров не был 104 | */ 105 | API cs_bool Heartbeat_Run(Heartbeat *self); 106 | 107 | /** 108 | * @brief Останавливает запросы к heartbeat серверу, 109 | * которые осуществлялись с помощью данного контекста. 110 | * 111 | * @param указатель на heartbeat 112 | */ 113 | API void Heartbeat_Close(Heartbeat *self); 114 | #endif // HEARTBEAT_H 115 | -------------------------------------------------------------------------------- /src/assoc.c: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | #include "client.h" 3 | #include "world.h" 4 | #include "assoc.h" 5 | #include "platform.h" 6 | #include "list.h" 7 | 8 | static AListField *headAssocType = NULL; 9 | 10 | typedef struct AssocBind { 11 | EAssocBindType type; 12 | AssocType id; 13 | } AssocBind; 14 | 15 | INL static AssocBind *GetBind(AListField *a) { 16 | return a ? (AssocBind *)a->value.ptr : NULL; 17 | } 18 | 19 | INL static AListField *AGetType(AssocType type, AssocBind **bind) { 20 | AListField *ptr = NULL; 21 | 22 | List_Iter(ptr, headAssocType) { 23 | *bind = GetBind(ptr); 24 | if((*bind)->id == type) return ptr; 25 | } 26 | 27 | *bind = NULL; 28 | return NULL; 29 | } 30 | 31 | INL static KListField *AGetNode(void *target, AssocBind *bind) { 32 | KListField *ptr; 33 | 34 | switch(bind->type) { 35 | case ASSOC_BIND_CLIENT: 36 | List_Iter(ptr, ((Client *)target)->headNode) 37 | if(ptr->key.num16 == bind->id) return ptr; 38 | break; 39 | case ASSOC_BIND_WORLD: 40 | List_Iter(ptr, ((World *)target)->headNode) 41 | if(ptr->key.num16 == bind->id) return ptr; 42 | break; 43 | 44 | default: return NULL; 45 | } 46 | 47 | return NULL; 48 | } 49 | 50 | AssocType Assoc_NewType(EAssocBindType bindto) { 51 | AssocBind *headBind = GetBind(headAssocType); 52 | AssocType id = headBind ? headBind->id + 1 : 0; 53 | if(id < 0) return ASSOC_INVALID_TYPE; 54 | AssocBind *next = (AssocBind *)Memory_Alloc(1, sizeof(AssocBind)); 55 | next->id = id, next->type = bindto; 56 | AList_AddField(&headAssocType, next); 57 | return id; 58 | } 59 | 60 | cs_bool Assoc_DelType(AssocType type) { 61 | AssocBind *bind = NULL; 62 | AListField *tptr = AGetType(type, &bind), *witer; 63 | if(!tptr) return false; 64 | 65 | switch(bind->type) { 66 | case ASSOC_BIND_CLIENT: 67 | for(ClientID id = 0; id < MAX_CLIENTS; id++) { 68 | if(Clients_List[id]) 69 | Assoc_Remove(Clients_List[id], type); 70 | } 71 | break; 72 | case ASSOC_BIND_WORLD: 73 | List_Iter(witer, World_Head) 74 | Assoc_Remove(witer->value.ptr, type); 75 | break; 76 | 77 | default: return false; 78 | } 79 | 80 | Memory_Free(bind); 81 | AList_Remove(&headAssocType, tptr); 82 | return true; 83 | } 84 | 85 | void *Assoc_AllocFor(void *target, AssocType type, cs_size num, cs_size size) { 86 | AssocBind *bind = NULL; 87 | if(!AGetType(type, &bind) || AGetNode(target, bind)) return NULL; 88 | void *memptr = Memory_TryAlloc(num, size); 89 | if(!memptr) return NULL; 90 | KListField *anode = NULL; 91 | 92 | switch(bind->type) { 93 | case ASSOC_BIND_CLIENT: 94 | anode = KList_AddField(&((Client *)target)->headNode, NULL, NULL); 95 | break; 96 | case ASSOC_BIND_WORLD: 97 | anode = KList_AddField(&((World *)target)->headNode, NULL, NULL); 98 | break; 99 | 100 | default: return NULL; 101 | } 102 | 103 | anode->value.ptr = memptr; 104 | anode->key.num16 = type; 105 | return memptr; 106 | } 107 | 108 | void *Assoc_GetPtr(void *target, AssocType type) { 109 | AssocBind *bind = NULL; 110 | if(!AGetType(type, &bind)) return NULL; 111 | KListField *anode = AGetNode(target, bind); 112 | if(anode) return anode->value.ptr; 113 | return NULL; 114 | } 115 | 116 | cs_bool Assoc_Remove(void *target, AssocType type) { 117 | AssocBind *bind = NULL; 118 | if(!AGetType(type, &bind)) return false; 119 | KListField *anode = AGetNode(target, bind); 120 | if(!anode) return false; 121 | Memory_Free(anode->value.ptr); 122 | 123 | switch(bind->type) { 124 | case ASSOC_BIND_CLIENT: 125 | KList_Remove(&((Client *)target)->headNode, anode); 126 | break; 127 | case ASSOC_BIND_WORLD: 128 | KList_Remove(&((World *)target)->headNode, anode); 129 | break; 130 | } 131 | 132 | return true; 133 | } 134 | -------------------------------------------------------------------------------- /src/types/protocol.h: -------------------------------------------------------------------------------- 1 | #ifndef PROTOCOLTYPES_H 2 | #define PROTOCOLTYPES_H 3 | #include "core.h" 4 | 5 | #define PROTOCOL_VERSION 0x07 6 | 7 | #define EXT_CLICKDIST 0x6DD2B567ul 8 | #define EXT_CUSTOMBLOCKS 0x98455F43ul 9 | #define EXT_HELDBLOCK 0x40C33F88ul 10 | #define EXT_TEXTHOTKEY 0x73BB9FBFul 11 | #define EXT_PLAYERLIST 0xBB0CD618ul 12 | #define EXT_ENVCOLOR 0x4C056274ul 13 | #define EXT_CUBOID 0xE45DA299ul 14 | #define EXT_BLOCKPERM 0xB2E8C3D6ul 15 | #define EXT_CHANGEMODEL 0xAE3AEBAAul 16 | #define EXT_MAPPROPS 0xB46CAFABul 17 | #define EXT_WEATHER 0x40501770ul 18 | #define EXT_MESSAGETYPE 0x7470960Eul 19 | #define EXT_HACKCTRL 0x6E4CED2Dul 20 | #define EXT_PLAYERCLICK 0x29442DBul 21 | #define EXT_CP437 0x27FBB82Ful 22 | #define EXT_LONGMSG 0x8535AB13ul 23 | #define EXT_BLOCKDEF 0xC6BAA7Bul 24 | #define EXT_BLOCKDEF2 0xEFB2BBECul 25 | #define EXT_BULKUPDATE 0x29509B8Ful 26 | #define EXT_TEXTCOLORS 0x56C393B8ul 27 | #define EXT_MAPASPECT 0xB3F9BDF0ul 28 | #define EXT_ENTPROP 0x5865D50Eul 29 | #define EXT_ENTPOS 0x37D3033Ful 30 | #define EXT_TWOWAYPING 0xBBC796E8ul 31 | #define EXT_INVORDER 0xEE0F7B71ul 32 | #define EXT_INSTANTMOTD 0x462BFA8Ful 33 | #define EXT_FASTMAP 0x7791DB5Ful 34 | #define EXT_SETHOTBAR 0xB8703914ul 35 | #define EXT_SETSPAWN 0x9149FD59ul 36 | #define EXT_VELCTRL 0xF8DF4FF7ul 37 | #define EXT_CUSTOMPARTS 0x0D732743 38 | #define EXT_PARTICLE 0x0D732743ul 39 | #define EXT_CUSTOMMODELS 0xB27EFA32ul 40 | #define EXT_PLUGINMESSAGE 0x59FA7285ul 41 | #define EXT_ENTTELEPORT 0x9af9cdc6ul 42 | #define EXT_LIGHTMODE 0x88c1ccb4ul 43 | 44 | typedef enum _EPacketID { 45 | // Vanilla packets 46 | PACKET_IDENTIFICATION = 0x00, 47 | PACKET_PING = 0x01, 48 | PACKET_LEVELINIT = 0x02, 49 | PACKET_LEVELCHUNK = 0x03, 50 | PACKET_LEVELFINAL = 0x04, 51 | PACKET_SETBLOCK_CLIENT = 0x05, 52 | PACKET_SETBLOCK_SERVER = 0x06, 53 | PACKET_ENTITYSPAWN = 0x07, 54 | PACKET_ENTITYTELEPORT = 0x08, 55 | PACKET_ENTITYUPDATEFULL = 0x09, 56 | PACKET_ENTITYUPDATEPOS = 0x0A, 57 | PACKET_ENTITYUPDATEORIENT = 0x0B, 58 | PACKET_ENTITYDESPAWN = 0x0C, 59 | PACKET_SENDMESSAGE = 0x0D, 60 | PACKET_ENTITYREMOVE = 0x0E, 61 | PACKET_USERUPDATE = 0x0F, 62 | 63 | // CPE packets 64 | PACKET_EXTINFO = 0x10, 65 | PACKET_EXTENTRY = 0x11, 66 | PACKET_CLICKDIST = 0x12, 67 | PACKET_CUSTOMBLOCKSLVL = 0x13, 68 | PACKET_HOLDTHIS = 0x14, 69 | PACKET_SETHOTKEY = 0x15, 70 | PACKET_NAMEADD = 0x16, 71 | PACKET_EXTENTITYADDv1, 72 | PACKET_NAMEREMOVE = 0x18, 73 | PACKET_SETENVCOLOR = 0x19, 74 | PACKET_SELECTIONADD = 0x1A, 75 | PACKET_SELECTIONREMOVE = 0x1B, 76 | PACKET_SETBLOCKPERMISSION = 0x1C, 77 | PACKET_ENTITYMODEL = 0x1D, 78 | PACKET_SETMAPAPPEARANCE = 0x1E, 79 | PACKET_SETENVWEATHER = 0x1F, 80 | PACKET_UPDATEHACKS = 0x20, 81 | PACKET_EXTENTITYADDv2 = 0x21, 82 | PACKET_PLAYERCLICKED = 0x22, 83 | PACKET_DEFINEBLOCK = 0x23, 84 | PACKET_UNDEFINEBLOCK = 0x24, 85 | PACKET_DEFINEBLOCKEXT = 0x25, 86 | PACKET_BULKBLOCKUPDATE = 0x26, 87 | PACKET_ADDTEXTCOLOR = 0x27, 88 | PACKET_ENVSETPACKURL = 0x28, 89 | PACKET_SETENVPROP = 0x29, 90 | PACKET_ENTITYPROPERTY = 0x2A, 91 | PACKET_TWOWAYPING = 0x2B, 92 | PACKET_SETINVORDER = 0x2C, 93 | PACKET_SETHOTBAR = 0x2D, 94 | PACKET_SETSPAWNPOINT = 0x2E, 95 | PACKET_SETVELOCITY = 0x2F, 96 | PACKET_DEFINEEFFECT = 0x30, 97 | PACKET_SPAWNEFFECT = 0x31, 98 | PACKET_DEFINEMODEL = 0x32, 99 | PACKET_DEFINEMODELPART = 0x33, 100 | PACKET_UNDEFINEMODEL = 0x34, 101 | PACKET_PLUGINMESSAGE = 0x35, 102 | PACKET_EXTENTITYTP = 0x36, 103 | PACKET_LIGHTINGMODE = 0x37 104 | } EPacketID; 105 | 106 | typedef struct _Packet { 107 | EPacketID id; 108 | cs_bool haveCPEImp; 109 | cs_uint16 size, extSize; 110 | cs_ulong exthash; 111 | cs_int32 extVersion; 112 | void *handler; 113 | void *extHandler; 114 | } Packet; 115 | 116 | typedef enum _ESetBlockMode { 117 | SETBLOCK_MODE_DESTROY, 118 | SETBLOCK_MODE_CREATE 119 | } ESetBlockMode; 120 | #endif 121 | -------------------------------------------------------------------------------- /src/command.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMAND_H 2 | #define COMMAND_H 3 | #include "core.h" 4 | #include "client.h" 5 | #include "types/list.h" 6 | #include "types/command.h" 7 | 8 | #define COMMAND_SETUSAGE(str) cs_str cmdUsage = str; 9 | #define COMMAND_PRINT(str) return String_Copy(ccdata->out, MAX_CMD_OUT, str) > 0 10 | #define COMMAND_PRINTF(f, ...) return String_FormatBuf(ccdata->out, MAX_CMD_OUT, f, ##__VA_ARGS__) > 0 11 | #define COMMAND_APPEND(str) String_Append(ccdata->out, MAX_CMD_OUT, str) 12 | #define COMMAND_GETARG(a, s, n) String_GetArgument(ccdata->args, a, s, n) 13 | 14 | #define COMMAND_APPENDF(buf, sz, fmt, ...) (\ 15 | String_FormatBuf(buf, sz, fmt, ##__VA_ARGS__), \ 16 | String_Append(ccdata->out, MAX_CMD_OUT, buf)) 17 | 18 | #define COMMAND_TESTOP() \ 19 | if(ccdata->caller && !Client_IsOP(ccdata->caller)) \ 20 | COMMAND_PRINT(Sstor_Get("CMD_NOPERM")); 21 | 22 | #define COMMAND_PRINTUSAGE \ 23 | COMMAND_PRINTF("Usage: %s", cmdUsage) 24 | 25 | #define COMMAND_FUNC(N) \ 26 | static cs_bool svcmd_##N(CommandCallData *ccdata) 27 | 28 | #define COMMAND_ADD(N, F, H) \ 29 | Command_Register(#N, H, (cmdFunc)svcmd_##N, F) 30 | 31 | #define COMMAND_REMOVE(N) \ 32 | Command_UnregisterByFunc((cmdFunc)svcmd_##N) 33 | 34 | #define Command_DeclareBunch(N) static CommandRegBunch N[] = 35 | #define Command_DeclarePubBunch(N) CommandRegBunch N[] = 36 | #define COMMAND_BUNCH_ADD(N, F, H) {#N, H, (cmdFunc)svcmd_##N, F} 37 | #define COMMAND_BUNCH_END {NULL, NULL, NULL, 0x00} 38 | 39 | #ifndef CORE_BUILD_PLUGIN 40 | extern AListField *Command_Head; /** Список зарегистрированных команд */ 41 | void Command_RegisterDefault(void); 42 | void Command_UnregisterAll(void); 43 | #endif 44 | 45 | /** 46 | * @brief Обрабатывает переданную строку как команду. 47 | * 48 | * @param str команда с аргументами (массив может измениться в процессе выполнения) 49 | * @param caller игрок, вызвавший команду 50 | * @return true - команда выполнена успешно, false - произошла какая-то ошибка 51 | */ 52 | API cs_bool Command_Handle(cs_char *str, Client *caller); 53 | 54 | /** 55 | * @brief Регистрирует новую команду. 56 | * 57 | * @param name название команды 58 | * @param descr описание команды 59 | * @param func функция команды 60 | * @param flags флаги команды 61 | * @return указатель на структуру команды 62 | */ 63 | API Command *Command_Register(cs_str name, cs_str descr, cmdFunc func, cs_byte flags); 64 | 65 | /** 66 | * @brief Удаляет зарегистрированную ранее команду. 67 | * 68 | * @param cmd указатель на структуру команды 69 | */ 70 | API void Command_Unregister(Command *cmd); 71 | 72 | API cs_bool Command_RegisterBunch(CommandRegBunch *bunch); 73 | 74 | API void Command_UnregisterBunch(CommandRegBunch *bunch); 75 | 76 | /** 77 | * @brief Возвращает указатель на структуру команды по её имени. 78 | * 79 | * @param name имя команды 80 | * @return указатель на структуру команды 81 | */ 82 | API Command *Command_GetByName(cs_str name); 83 | 84 | /** 85 | * @brief Удаляет зарегистрированную ранее команду. 86 | * 87 | * @param func функция команды 88 | */ 89 | API void Command_UnregisterByFunc(cmdFunc func); 90 | 91 | /** 92 | * @brief Возвращает имя команды. 93 | * 94 | * @param cmd указатель на структуру команды 95 | * @return имя команды 96 | */ 97 | API cs_str Command_GetName(Command *cmd); 98 | 99 | /** 100 | * @brief Устанавливает короткое имя для команды. 101 | * Короткое имя не может быть длиннее 6 символов. 102 | * 103 | * @param cmd указатель на структуру команды 104 | */ 105 | API cs_bool Command_SetAlias(Command *cmd, cs_str alias); 106 | 107 | /** 108 | * @brief Присваивает команде указатель для различных целей. 109 | * 110 | * @param cmd указатель на структуру команды 111 | * @param ud указатель на данные 112 | */ 113 | API void Command_SetUserData(Command *cmd, void *ud); 114 | 115 | /** 116 | * @brief Возвращает ранее установленный команде указатель. 117 | * 118 | * @param cmd указатель на структуру команды 119 | * @return указатель на данные 120 | */ 121 | API void *Command_GetUserData(Command *cmd); 122 | #endif // COMMAND_H 123 | -------------------------------------------------------------------------------- /src/types/block.h: -------------------------------------------------------------------------------- 1 | #ifndef BLOCKTYPES_H 2 | #define BLOCKTYPES_H 3 | #include "core.h" 4 | #include "types/world.h" 5 | 6 | /** 7 | * @name BDF_* 8 | * 9 | * Флаги для динамических блоков. 10 | * 11 | */ 12 | /* @{ */ 13 | #define BDF_EXTENDED BIT(0) /** Блок использует расширенную спецификацию */ 14 | #define BDF_RESERVED BIT(1) /** Зарезервированный флаг, пока не используется */ 15 | #define BDF_UPDATED BIT(2) /** Блок был разослан клиентам */ 16 | #define BDF_UNDEFINED BIT(3) /** Блок удалён */ 17 | /* @} */ 18 | 19 | /** 20 | * @brief Перечисление граней блока. 21 | * 22 | */ 23 | typedef enum _EBlockFace { 24 | BLOCK_FACE_AWAY_X, /** Удалено от оси X */ 25 | BLOCK_FACE_TOWARDS_X, /** В направлении оси X */ 26 | BLOCK_FACE_AWAY_Y, /** Удалено от начала координат Y (верхняя грань) */ 27 | BLOCK_FACE_TOWARDS_Y, /** В направлении начала координат Y (нижняя грань) */ 28 | BLOCK_FACE_AWAY_Z, /** Удалено от оси Z */ 29 | BLOCK_FACE_TOWARDS_Z /** В направлении оси Z */ 30 | } EBlockFace; 31 | 32 | /** 33 | * @brief Перечисление типов блоков. 34 | * 35 | */ 36 | typedef enum _EBlockIDs { 37 | BLOCK_AIR = 0, /** Воздух */ 38 | BLOCK_STONE = 1, /** Камень */ 39 | BLOCK_GRASS = 2, /** Трава */ 40 | BLOCK_DIRT = 3, /** Земля */ 41 | BLOCK_COBBLE = 4, /** Булыжник */ 42 | BLOCK_WOOD = 5, /** Доски */ 43 | BLOCK_SAPLING = 6, /** Росток */ 44 | BLOCK_BEDROCK = 7, /** Бедрок */ 45 | BLOCK_WATER = 8, /** Текущая вода */ 46 | BLOCK_WATER_STILL = 9, /** Стоячая вода */ 47 | BLOCK_LAVA = 10, /** Текущая лава */ 48 | BLOCK_LAVA_STILL = 11, /** Стоячая лава */ 49 | BLOCK_SAND = 12, /** Песок */ 50 | BLOCK_GRAVEL = 13, /** Гравий */ 51 | BLOCK_GOLD_ORE = 14, /** Золотая руда */ 52 | BLOCK_IRON_ORE = 15, /** Железная руда */ 53 | BLOCK_COAL_ORE = 16, /** Угольная руда */ 54 | BLOCK_LOG = 17, /** Дерево */ 55 | BLOCK_LEAVES = 18, /** Листва */ 56 | BLOCK_SPONGE = 19, /** Губка */ 57 | BLOCK_GLASS = 20, /** Стекло */ 58 | BLOCK_RED = 21, /** Красная шерсть */ 59 | BLOCK_ORANGE = 22, /** Оранжевая шерсть */ 60 | BLOCK_YELLOW = 23, /** Желтая шерсть */ 61 | BLOCK_LIME = 24, /** Лаймовая шерсть */ 62 | BLOCK_GREEN = 25, /** Зелёная шерсть */ 63 | BLOCK_TEAL = 26, /** Голубо-зелёная шерсть */ 64 | BLOCK_AQUA = 27, /** Шерсть цвета морской воды */ 65 | BLOCK_CYAN = 28, /** Голубая шерсть */ 66 | BLOCK_BLUE = 29, /** Синяя шерсть */ 67 | BLOCK_INDIGO = 30, /** Шерсть цвета индиго */ 68 | BLOCK_VIOLET = 31, /** Фиолетовая шерсть */ 69 | BLOCK_MAGENTA = 32, /** Пурпурная шерсть */ 70 | BLOCK_PINK = 33, /** Розовая шерсть */ 71 | BLOCK_BLACK = 34, /** Чёрная шерсть */ 72 | BLOCK_GRAY = 35, /** Серая шерсть */ 73 | BLOCK_WHITE = 36, /** Белая шерсть */ 74 | BLOCK_DANDELION = 37, /** Одуванчик */ 75 | BLOCK_ROSE = 38, /** Роза */ 76 | BLOCK_BROWN_SHROOM = 39, /** Коричневый гриб */ 77 | BLOCK_RED_SHROOM = 40, /** Красный гриб */ 78 | BLOCK_GOLD = 41, /** Золото */ 79 | BLOCK_IRON = 42, /** Железо */ 80 | BLOCK_DOUBLE_SLAB = 43, /** Двойная плита */ 81 | BLOCK_SLAB = 44, /** Одиночная плита */ 82 | BLOCK_BRICK = 45, /** Кирпич */ 83 | BLOCK_TNT = 46, /** Динамит */ 84 | BLOCK_BOOKSHELF = 47, /** Книжная полка */ 85 | BLOCK_MOSSY_ROCKS = 48, /** Замшелый булыжник */ 86 | BLOCK_OBSIDIAN = 49, /** Обсидиан */ 87 | 88 | /** Блоки из CPE расширения CustomBlocks */ 89 | BLOCK_COBBLESLAB = 50, 90 | BLOCK_ROPE = 51, 91 | BLOCK_SANDSTONE = 52, 92 | BLOCK_SNOW = 53, 93 | BLOCK_FIRE = 54, 94 | BLOCK_LIGHTPINK = 55, 95 | BLOCK_FORESTGREEN = 56, 96 | BLOCK_BROWN = 57, 97 | BLOCK_DEEPBLUE = 58, 98 | BLOCK_TURQUOISE = 59, 99 | BLOCK_ICE = 60, 100 | BLOCK_CERAMICTILE = 61, 101 | BLOCK_MAGMA = 62, 102 | BLOCK_PILLAR = 63, 103 | BLOCK_CRATE = 64, 104 | BLOCK_STONEBRICK = 65, 105 | 106 | BLOCK_DEFAULT_COUNT 107 | } EBlockIDs; 108 | 109 | typedef struct _BulkBlockUpdate { 110 | World *world; /** Мир, игрокам которого будет отослан пакет */ 111 | cs_bool autosend; /** Автоматически отправлять пакет в случае переполнения буфера блоков */ 112 | struct _BBUData { 113 | cs_byte count; /** Количество блоков в буфере */ 114 | cs_byte offsets[1024]; /** Смещения блоков в массиве мира */ 115 | BlockID ids[256]; /** Номера блоков */ 116 | } data; /** Буфер блоков */ 117 | } BulkBlockUpdate; 118 | #endif 119 | -------------------------------------------------------------------------------- /src/platform.h: -------------------------------------------------------------------------------- 1 | #ifndef PLATFORM_H 2 | #define PLATFORM_H 3 | #include "core.h" 4 | #include "types/platform.h" 5 | 6 | #define Memory_Zero(p, c) Memory_Fill(p, c, 0) 7 | #define THREAD_FUNC(N) static TRET N(TARG param) 8 | #define THREAD_PUBFUNC(N) TRET N(TARG param) 9 | 10 | #ifndef CORE_BUILD_PLUGIN 11 | cs_bool Memory_Init(void); 12 | void Memory_Uninit(void); 13 | 14 | cs_bool Console_BindSignalHandler(TSHND handler); 15 | #endif 16 | 17 | API void *Memory_TryAlloc(cs_size num, cs_size size); 18 | API void *Memory_TryRealloc(void *oldptr, cs_size new); 19 | API cs_size Memory_GetSize(void *ptr); 20 | API void *Memory_Alloc(cs_size num, cs_size size); 21 | API void *Memory_Realloc(void *oldptr, cs_size new); 22 | API void Memory_Copy(void *dst, const void *src, cs_size count); 23 | API void Memory_Fill(void *dst, cs_size count, cs_byte val); 24 | API cs_bool Memory_Compare(const cs_byte *src1, const cs_byte *src2, cs_size len); 25 | API void Memory_Free(void *ptr); 26 | 27 | API cs_bool Iter_Init(DirIter *iter, cs_str path, cs_str ext); 28 | API cs_bool Iter_Next(DirIter *iter); 29 | API void Iter_Close(DirIter *iter); 30 | 31 | API cs_error File_Access(cs_str path, cs_int32 mode); 32 | API cs_bool File_Rename(cs_str path, cs_str newpath); 33 | API cs_file File_Open(cs_str path, cs_str mode); 34 | API cs_file File_ProcOpen(cs_str cmd, cs_str mode); 35 | API cs_size File_Read(void *ptr, cs_size size, cs_size count, cs_file fp); 36 | API cs_int32 File_ReadLine(cs_file fp, cs_char *line, cs_size len); 37 | API cs_size File_Write(const void *ptr, cs_size size, cs_size count, cs_file fp); 38 | API cs_int32 File_GetChar(cs_file fp); 39 | API cs_int32 File_Error(cs_file fp); 40 | API cs_bool File_IsEnd(cs_file fp); 41 | API cs_int32 File_WriteFormat(cs_file fp, cs_str fmt, ...); 42 | API cs_bool File_Flush(cs_file fp); 43 | API cs_long File_Seek(cs_file fp, cs_long offset, cs_int32 origin); 44 | API cs_bool File_Close(cs_file fp); 45 | API cs_bool File_ProcClose(cs_file fp); 46 | 47 | API cs_bool Directory_Exists(cs_str dir); 48 | API cs_bool Directory_Create(cs_str dir); 49 | API cs_bool Directory_Ensure(cs_str dir); 50 | API cs_bool Directory_SetCurrentDir(cs_str path); 51 | 52 | #define DLib_List(names) ((cs_str const[]){names, NULL}) 53 | cs_bool DLib_Load(cs_str path, void **lib); 54 | cs_bool DLib_Unload(void *lib); 55 | cs_char *DLib_GetError(cs_char *buf, cs_size len); 56 | cs_bool DLib_GetSym(void *lib, cs_str sname, void *sym); 57 | cs_bool DLib_LoadAll(cs_str const lib[], cs_str const symlist[], void **ctx); 58 | 59 | cs_bool Socket_Init(void); 60 | void Socket_Uninit(void); 61 | API Socket Socket_New(void); 62 | API cs_bool Socket_IsFatal(void); 63 | API cs_bool Socket_IsLocal(cs_ulong addr); 64 | API cs_error Socket_GetError(void); 65 | API cs_ulong Socket_AvailData(Socket n); 66 | API cs_bool Socket_SetNonBlocking(Socket n, cs_bool state); 67 | API cs_int32 Socket_SetAddr(struct sockaddr_in *ssa, cs_str ip, cs_uint16 port); 68 | API cs_bool Socket_SetAddrGuess(struct sockaddr_in *ssa, cs_str host, cs_uint16 port); 69 | API cs_bool Socket_Bind(Socket sock, struct sockaddr_in *ssa); 70 | API cs_bool Socket_Connect(Socket sock, struct sockaddr_in *ssa); 71 | API Socket Socket_Accept(Socket sock, struct sockaddr_in *addr); 72 | API cs_int32 Socket_Receive(Socket sock, cs_char *buf, cs_int32 len, cs_int32 flags); 73 | API cs_int32 Socket_Send(Socket sock, const cs_char *buf, cs_int32 len); 74 | API cs_bool Socket_Shutdown(Socket sock, cs_int32 how); 75 | API void Socket_Close(Socket sock); 76 | 77 | API Thread Thread_Create(TFUNC func, const TARG param, cs_bool detach); 78 | API cs_bool Thread_IsValid(Thread th); 79 | API cs_bool Thread_Signal(Thread th, cs_int32 sig); 80 | API void Thread_Detach(Thread th); 81 | API void Thread_Join(Thread th); 82 | API void Thread_Sleep(cs_uint32 ms); 83 | API cs_error Thread_GetError(void); 84 | 85 | API Mutex *Mutex_Create(void); 86 | API void Mutex_Free(Mutex *mtx); 87 | API void Mutex_Lock(Mutex *mtx); 88 | API void Mutex_Unlock(Mutex *mtx); 89 | 90 | API Waitable *Waitable_Create(void); 91 | API void Waitable_Free(Waitable *wte); 92 | API void Waitable_Signal(Waitable *wte); 93 | API void Waitable_Wait(Waitable *wte); 94 | API cs_bool Waitable_TryWait(Waitable *wte, cs_ulong timeout); 95 | API void Waitable_Reset(Waitable *wte); 96 | 97 | API cs_int32 Time_Format(cs_char *buf, cs_size len); 98 | API cs_uint64 Time_GetMSec(void); 99 | API cs_double Time_GetMSecD(void); 100 | 101 | API void Process_Exit(cs_int32 ecode); 102 | #endif // PLATFORM_H 103 | -------------------------------------------------------------------------------- /src/hash.c: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | #include "hash.h" 3 | #include "platform.h" 4 | 5 | #if defined(HASH_USE_WINCRYPT_BACKEND) 6 | static HCRYPTPROV hCryptProvider = 0; 7 | 8 | static cs_str csymlist[] = { 9 | "CryptAcquireContextA", "CryptReleaseContext", 10 | "CryptCreateHash", "CryptHashData", 11 | "CryptGetHashParam", "CryptDestroyHash", 12 | NULL 13 | }; 14 | 15 | static struct _CryptLib { 16 | void *lib; 17 | 18 | BOOL(__stdcall *AcquireContext)(HCRYPTPROV *, cs_str, cs_str, cs_ulong, cs_ulong); 19 | BOOL(__stdcall *ReleaseContext)(HCRYPTPROV, cs_ulong); 20 | BOOL(__stdcall *CreateHash)(HCRYPTPROV, ALG_ID, HCRYPTKEY, cs_ulong, HCRYPTHASH *); 21 | BOOL(__stdcall *HashData)(HCRYPTHASH, const cs_byte *, cs_ulong, cs_ulong); 22 | BOOL(__stdcall *GetHashParam)(HCRYPTHASH, cs_ulong, cs_byte *, cs_ulong *, cs_ulong); 23 | BOOL(__stdcall *DestroyHash)(HCRYPTHASH); 24 | } Crypt; 25 | 26 | INL static cs_bool InitBackend(void) { 27 | if(!Crypt.lib && !DLib_LoadAll(DLib_List("advapi32.dll"), csymlist, (void **)&Crypt)) 28 | return false; 29 | 30 | if(!hCryptProvider) 31 | return (cs_bool)Crypt.AcquireContext( 32 | &hCryptProvider, NULL, 33 | MS_DEF_PROV, PROV_RSA_FULL, 34 | CRYPT_VERIFYCONTEXT 35 | ); 36 | 37 | return true; 38 | } 39 | 40 | void Hash_Uninit(void) { 41 | if(hCryptProvider) Crypt.ReleaseContext(hCryptProvider, 0); 42 | DLib_Unload(Crypt.lib); 43 | Memory_Zero(&Crypt, sizeof(Crypt)); 44 | } 45 | 46 | static cs_bool InitAlg(HASH_CTX *ctx, ALG_ID alg) { 47 | if(!Crypt.lib && !InitBackend()) return false; 48 | return (cs_bool)Crypt.CreateHash(hCryptProvider, alg, 0, 0, &ctx->hash); 49 | } 50 | 51 | INL static cs_bool UpdateHash(HASH_CTX *ctx, const void *data, cs_ulong len) { 52 | if(!Crypt.lib) return false; 53 | return (cs_bool)Crypt.HashData(ctx->hash, data, len, 0); 54 | } 55 | 56 | INL static cs_bool FinalHash(void *hash, HASH_CTX *ctx) { 57 | if(!Crypt.lib) return false; 58 | return Crypt.GetHashParam(ctx->hash, HP_HASHVAL, hash, &ctx->hashLen, 0) && 59 | Crypt.DestroyHash(ctx->hash); 60 | } 61 | 62 | cs_bool SHA1_Start(SHA_CTX *ctx) { 63 | ctx->hashLen = 20; 64 | return InitAlg(ctx, CALG_SHA1); 65 | } 66 | 67 | cs_bool SHA1_PushData(SHA_CTX *ctx, const void *data, cs_ulong len) { 68 | return UpdateHash(ctx, data, len); 69 | } 70 | 71 | cs_bool SHA1_End(cs_byte *hash, SHA_CTX *ctx) { 72 | return FinalHash(hash, ctx); 73 | } 74 | 75 | cs_bool MD5_Start(MD5_CTX *ctx) { 76 | ctx->hashLen = 16; 77 | return InitAlg(ctx, CALG_MD5); 78 | } 79 | 80 | cs_bool MD5_PushData(MD5_CTX *ctx, const void *data, cs_ulong len) { 81 | return UpdateHash(ctx, data, len); 82 | } 83 | 84 | cs_bool MD5_End(cs_byte *hash, MD5_CTX *ctx) { 85 | return FinalHash(hash, ctx); 86 | } 87 | #elif defined(HASH_USE_CRYPTO_BACKEND) 88 | static cs_str csymlist[] = { 89 | "SHA1_Init", "SHA1_Update", "SHA1_Final", 90 | "MD5_Init", "MD5_Update", "MD5_Final", 91 | NULL 92 | }; 93 | 94 | static struct _CryptLib { 95 | void *lib; 96 | 97 | cs_bool(*SHA1Init)(SHA_CTX *); 98 | cs_bool(*SHA1Update)(SHA_CTX *, const void *data, cs_ulong len); 99 | cs_bool(*SHA1Final)(cs_byte *, SHA_CTX *); 100 | 101 | cs_bool(*MD5Init)(MD5_CTX *); 102 | cs_bool(*MD5Update)(MD5_CTX *, const void *data, cs_ulong len); 103 | cs_bool(*MD5Final)(cs_byte *, MD5_CTX *); 104 | } Crypt; 105 | 106 | static cs_str cryptodll[] = { 107 | #if defined(CORE_USE_WINDOWS) 108 | "crypto.dll", 109 | "libeay32.dll", 110 | #elif defined(CORE_USE_UNIX) 111 | "libcrypto.35." DLIB_EXT, 112 | "libcrypto." DLIB_EXT ".1.1", 113 | "libcrypto." DLIB_EXT, 114 | #else 115 | # error This file wants to be hacked 116 | #endif 117 | NULL 118 | }; 119 | 120 | cs_bool Hash_Init(void) { 121 | return Crypt.lib != NULL || DLib_LoadAll(cryptodll, csymlist, (void **)&Crypt); 122 | } 123 | 124 | void Hash_Uninit(void) { 125 | if(!Crypt.lib) return; 126 | Crypt.SHA1Init = NULL; 127 | Crypt.SHA1Update = NULL; 128 | Crypt.SHA1Final = NULL; 129 | Crypt.MD5Init = NULL; 130 | Crypt.MD5Update = NULL; 131 | Crypt.MD5Final = NULL; 132 | DLib_Unload(Crypt.lib); 133 | Crypt.lib = NULL; 134 | } 135 | 136 | cs_bool SHA1_Start(SHA_CTX *ctx) { 137 | if(!Crypt.lib && !Hash_Init()) return false; 138 | return Crypt.SHA1Init(ctx); 139 | } 140 | 141 | cs_bool SHA1_PushData(SHA_CTX *ctx, const void *data, cs_ulong len) { 142 | if(!Crypt.lib) return false; 143 | return Crypt.SHA1Update(ctx, data, len); 144 | } 145 | 146 | cs_bool SHA1_End(cs_byte *hash, SHA_CTX *ctx) { 147 | if(!Crypt.lib) return false; 148 | return Crypt.SHA1Final(hash, ctx); 149 | } 150 | 151 | cs_bool MD5_Start(MD5_CTX *ctx) { 152 | if(!Crypt.lib && !Hash_Init()) return false; 153 | return Crypt.MD5Init(ctx); 154 | } 155 | 156 | cs_bool MD5_PushData(MD5_CTX *ctx, const void *data, cs_ulong len) { 157 | if(!Crypt.lib) return false; 158 | return Crypt.MD5Update(ctx, data, len); 159 | } 160 | 161 | cs_bool MD5_End(cs_byte *hash, MD5_CTX *ctx) { 162 | if(!Crypt.lib) return false; 163 | return Crypt.MD5Final(hash, ctx); 164 | } 165 | #else 166 | # error This file wants to be hacked 167 | #endif 168 | -------------------------------------------------------------------------------- /src/netbuffer.c: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | #include "platform.h" 3 | #include "netbuffer.h" 4 | #include "websock.h" 5 | 6 | static cs_bool Ensure(GrowingBuffer *self, cs_uint32 size) { 7 | cs_uint32 required = self->offset + size; 8 | if(self->maxsize > 0 && self->maxsize < required) 9 | return false; 10 | 11 | if(self->size < required) { 12 | if(self->maxsize) 13 | required = min(required + GROWINGBUFFER_ADDITIONAL, self->maxsize); 14 | else 15 | required += GROWINGBUFFER_ADDITIONAL; 16 | 17 | if(self->buffer) 18 | self->buffer = Memory_Realloc(self->buffer, required); 19 | else 20 | self->buffer = Memory_Alloc(1, required); 21 | 22 | self->size = required; 23 | } 24 | 25 | return true; 26 | } 27 | 28 | static INL void Cleanup(GrowingBuffer *self) { 29 | if(self->buffer) { 30 | Memory_Free(self->buffer); 31 | self->buffer = NULL; 32 | self->offset = 0; 33 | self->size = 0; 34 | } 35 | } 36 | 37 | void NetBuffer_Init(NetBuffer *nb, Socket sock) { 38 | Ensure(&nb->write, 1); 39 | Ensure(&nb->read, 1); 40 | nb->fd = sock; 41 | } 42 | 43 | cs_bool NetBuffer_Process(NetBuffer *nb) { 44 | cs_ulong avail = Socket_AvailData(nb->fd); 45 | if(avail > 0) { 46 | Ensure(&nb->read, avail); 47 | cs_char *data = nb->read.buffer + nb->read.offset; 48 | if(Socket_Receive(nb->fd, data, avail, 0) != -1) 49 | nb->read.offset += avail; 50 | else if(Socket_IsFatal()) { 51 | nb->closed = true; 52 | return false; 53 | } 54 | } else { 55 | cs_char unused; 56 | cs_int32 ret = Socket_Receive(nb->fd, &unused, 1, MSG_PEEK); 57 | if(ret == 0 || (ret == -1 && Socket_IsFatal())) { 58 | nb->closed = true; 59 | return false; 60 | } 61 | } 62 | 63 | avail = NetBuffer_AvailWrite(nb); 64 | if(avail > 0) { 65 | if(nb->asframe) { 66 | if(nb->framesize == 0) { 67 | cs_int32 hdrlen = 0; 68 | nb->framesize = min(avail, WEBSOCK_FRAME_MAXSIZE); 69 | if(WebSock_WriteHeader(nb->fd, 0x02, nb->framesize, &hdrlen) != hdrlen) { 70 | if(Socket_IsFatal()) { 71 | nb->closed = true; 72 | return false; 73 | } 74 | 75 | nb->framesize = 0; 76 | return true; 77 | } 78 | } 79 | 80 | avail = min(nb->framesize, avail); 81 | } 82 | 83 | cs_char *data = nb->write.buffer + nb->cwrite; 84 | cs_int32 sent = Socket_Send(nb->fd, data, avail); 85 | if(sent > 0) { 86 | nb->cwrite += sent; 87 | if(nb->asframe) nb->framesize -= sent; 88 | if(NetBuffer_AvailWrite(nb) == 0) { 89 | if(nb->shutdown) Socket_Shutdown(nb->fd, SD_SEND); 90 | if(nb->wsupgrade) nb->asframe = true; 91 | nb->write.offset = 0; 92 | nb->cwrite = 0; 93 | } 94 | } else if(Socket_IsFatal()) { 95 | nb->closed = true; 96 | return false; 97 | } 98 | } 99 | 100 | return true; 101 | } 102 | 103 | cs_char *NetBuffer_PeekRead(NetBuffer *nb, cs_uint32 point) { 104 | if(nb->cread + point > nb->read.offset) return NULL; 105 | return nb->read.buffer + nb->cread; 106 | } 107 | 108 | // TODO: Почистить эту функцию от непотребностей 109 | cs_int32 NetBuffer_ReadLine(NetBuffer *nb, cs_char *buffer, cs_uint32 buflen) { 110 | cs_char *data = nb->read.buffer + nb->cread; 111 | if(!data) { 112 | nb->read.offset = 0; 113 | return -2; 114 | } 115 | 116 | cs_uint32 avail = nb->read.offset - nb->cread; 117 | if(avail == 0) return -2; 118 | 119 | cs_uint32 bufpos = 0; 120 | for(cs_uint32 i = 0; i < buflen; i++) { 121 | if(i > avail) return -2; 122 | 123 | if(data[i] == '\n') { 124 | nb->cread += (i + 1); 125 | buffer[bufpos] = '\0'; 126 | return (cs_int32)bufpos; 127 | } else if(data[i] != '\r') 128 | buffer[bufpos++] = data[i]; 129 | } 130 | 131 | return -1; 132 | } 133 | 134 | cs_char *NetBuffer_Read(NetBuffer *nb, cs_uint32 len) { 135 | cs_char *ptr = nb->read.buffer + nb->cread; 136 | if(!ptr) { 137 | nb->read.offset = 0; 138 | return false; 139 | } 140 | nb->cread += len; 141 | return ptr; 142 | } 143 | 144 | cs_char *NetBuffer_StartWrite(NetBuffer *nb, cs_uint32 dlen) { 145 | if(!Ensure(&nb->write, dlen)) return false; 146 | return nb->write.buffer + nb->write.offset; 147 | } 148 | 149 | cs_bool NetBuffer_EndWrite(NetBuffer *nb, cs_uint32 size) { 150 | cs_uint32 newsize = nb->write.offset + size; 151 | if(newsize > nb->write.size) return false; 152 | nb->write.offset = newsize; 153 | return true; 154 | } 155 | 156 | cs_uint32 NetBuffer_AvailRead(NetBuffer *nb) { 157 | return nb->read.offset - nb->cread; 158 | } 159 | 160 | cs_uint32 NetBuffer_AvailWrite(NetBuffer *nb) { 161 | return nb->write.offset - nb->cwrite; 162 | } 163 | 164 | cs_bool NetBuffer_Shutdown(NetBuffer *nb) { 165 | if(!NetBuffer_IsAlive(nb) || !NetBuffer_IsValid(nb)) return false; 166 | nb->shutdown = true; 167 | return true; 168 | } 169 | 170 | cs_bool NetBuffer_IsValid(NetBuffer *nb) { 171 | return nb->fd != INVALID_SOCKET; 172 | } 173 | 174 | cs_bool NetBuffer_IsAlive(NetBuffer *nb) { 175 | return nb->closed == false; 176 | } 177 | 178 | void NetBuffer_ForceClose(NetBuffer *nb) { 179 | if(nb->fd != INVALID_SOCKET) Socket_Close(nb->fd); 180 | Cleanup(&nb->write); 181 | Cleanup(&nb->read); 182 | nb->closed = true; 183 | } 184 | -------------------------------------------------------------------------------- /src/consoleio.c: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | #include "log.h" 3 | #include "server.h" 4 | #include "command.h" 5 | #include "consoleio.h" 6 | #include "strstor.h" 7 | #include "client.h" 8 | #include "world.h" 9 | #include "list.h" 10 | #include "platform.h" 11 | 12 | static struct { 13 | void *lib; 14 | 15 | cs_char *(*start)(cs_str *prompt); 16 | cs_int32 (*clear)(void); 17 | cs_int32 (*reset)(void); 18 | void (*draw)(void); 19 | cs_char **(*match)(cs_str text, char *(*)(cs_str, cs_int32)); 20 | void (*tohistory)(cs_str text); 21 | 22 | void **attemptfunc; 23 | cs_int32 *complatt; 24 | 25 | void (*prep)(cs_int32 meta); 26 | void (*deprep)(void); 27 | } ReadLine; 28 | 29 | static cs_str const syms[] = { 30 | "readline", "rl_clear_visible_line", 31 | "rl_reset_line_state", "rl_redisplay", 32 | "rl_completion_matches", "add_history", 33 | 34 | "rl_attempted_completion_function", 35 | "rl_attempted_completion_over", 36 | 37 | "rl_prep_terminal", "rl_deprep_terminal", 38 | 39 | NULL 40 | }; 41 | 42 | /* 43 | * Windows не поддерживает libreadline. 44 | * Имеется порт 5ой, но и его не получится 45 | * заставить работать. Так как нужна как 46 | * минимум 7ая версия ридлайна. 47 | * 48 | * В libreadline.dylib.8 по какой-то 49 | * мистической причине отсутствует символ 50 | * rl_clear_visible_line, соответственно 51 | * библиотека не будет использоваться. 52 | * Нужно либо придумать способ обойтись 53 | * без этой функции, либо же забить. 54 | * Профиты от первого варианта: 55 | * - ConsoleIO начнёт рабоать под MacOS 56 | * - Станет возможной поддержка версий 57 | * библиотеки libreadline ниже 8. 58 | */ 59 | 60 | static cs_str const rllib[] = { 61 | #ifdef CORE_USE_UNIX 62 | "libreadline." DLIB_EXT ".8", 63 | "libreadline." DLIB_EXT ".7", 64 | // Ниже 7ой версии отсутствуют некоторые API 65 | "libreadline." DLIB_EXT "", // Опасненько 66 | #endif 67 | NULL 68 | }; 69 | 70 | Thread inputThread = (Thread)NULL; 71 | cs_bool rlalive = false, 72 | rlupdated = false; 73 | 74 | static cs_char *cmd_generator(cs_str text, cs_int32 state) { 75 | cs_size tlen = String_Length(text); 76 | if(tlen < 1) return NULL; 77 | AListField *field; 78 | 79 | List_Iter(field, Command_Head) { 80 | Command *cmd = field->value.ptr; 81 | if(String_CaselessCompare2(cmd->name, text, tlen)) { 82 | if(state-- > 0) continue; 83 | return (cs_char *)String_AllocCopy(cmd->name); 84 | } 85 | } 86 | 87 | return NULL; 88 | } 89 | 90 | static cs_char *arg_generator(cs_str text, cs_int32 state) { 91 | cs_size tlen = String_Length(text); 92 | if(tlen < 1) return NULL; 93 | 94 | for(ClientID i = 0; i < MAX_CLIENTS; i++) { 95 | Client *client = Clients_List[i]; 96 | if(!client) continue; 97 | cs_str name = Client_GetName(client); 98 | 99 | if(String_CaselessCompare2(name, text, tlen)) { 100 | if(state-- > 0) continue; 101 | return (cs_char *)String_AllocCopy(name); 102 | } 103 | } 104 | 105 | AListField *field; 106 | List_Iter(field, World_Head) { 107 | World *world = field->value.ptr; 108 | cs_str name = World_GetName(world); 109 | if(String_CaselessCompare2(name, text, tlen)) { 110 | if(state-- > 0) continue; 111 | return (cs_char *)String_AllocCopy(name); 112 | } 113 | } 114 | 115 | return NULL; 116 | } 117 | 118 | static char **cmd_completion(cs_str in, cs_int32 start, cs_int32 end) { 119 | (void)end; 120 | *ReadLine.complatt = 1; 121 | if(start == 0) 122 | return ReadLine.match(in, cmd_generator); 123 | else 124 | return ReadLine.match(in, arg_generator); 125 | } 126 | 127 | void ConsoleIO_PrePrint(void) { 128 | if(ReadLine.lib && rlalive) { 129 | ReadLine.clear(); 130 | ReadLine.draw(); 131 | // TODO: Избавиться от этого непотребства 132 | File_Write("\x1B[2K\r", 5, 1, stderr); 133 | rlupdated = true; 134 | } 135 | } 136 | 137 | void ConsoleIO_AfterPrint(void) { 138 | if(ReadLine.lib && rlupdated) { 139 | rlupdated = false; 140 | ReadLine.reset(); 141 | ReadLine.draw(); 142 | } 143 | } 144 | 145 | static TSHND_RET sighand(TSHND_PARAM signal) { 146 | if(signal == CONSOLEIO_TERMINATE) Server_Active = false; 147 | return TSHND_OK; 148 | } 149 | 150 | THREAD_FUNC(ConsoleIOThread) { 151 | (void)param; 152 | while(Server_Active) { 153 | if(ReadLine.lib) { 154 | rlalive = true; 155 | cs_char *buf = ReadLine.start(NULL); 156 | rlalive = false; 157 | if(buf) { 158 | if(*buf != '\0') { 159 | ReadLine.tohistory(buf); 160 | if(Command_Handle(buf, NULL)) { 161 | Memory_Free(buf); 162 | continue; 163 | } 164 | } 165 | Memory_Free(buf); 166 | } 167 | } else { 168 | cs_char buf[192]; 169 | if(File_ReadLine(stdin, buf, 192) > 0) 170 | if(Command_Handle(buf, NULL)) continue; 171 | } 172 | 173 | Log_Info(Sstor_Get("CMD_UNK")); 174 | } 175 | 176 | return 0; 177 | } 178 | 179 | cs_bool ConsoleIO_Init(void) { 180 | if(DLib_LoadAll(rllib, syms, (void **)&ReadLine)) 181 | *ReadLine.attemptfunc = (void *)cmd_completion; 182 | 183 | inputThread = Thread_Create(ConsoleIOThread, NULL, false); 184 | return Console_BindSignalHandler(sighand); 185 | } 186 | 187 | void ConsoleIO_Uninit(void) { 188 | if(ReadLine.lib) 189 | ReadLine.deprep(); 190 | } 191 | -------------------------------------------------------------------------------- /src/block.c: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | #include "world.h" 3 | #include "client.h" 4 | #include "block.h" 5 | #include "platform.h" 6 | #include "list.h" 7 | 8 | static cs_str const defaultBlockNames[BLOCK_DEFAULT_COUNT] = { 9 | "Air", "Stone", "Grass", "Dirt", 10 | "Cobblestone", "Wood", "Sapling", 11 | "Bedrock", "Water", "Still water", 12 | "Lava", "Still lava", "Sand", 13 | "Gravel", "Gold ore", "Iron ore", 14 | "Coal ore", "Log", "Leaves", 15 | "Sponge", "Glass", "Red", "Orange", 16 | "Yellow", "Lime", "Green", "Teal", 17 | "Aqua", "Cyan", "Blue", "Indigo", 18 | "Violet", "Magenta", "Pink", 19 | "Black", "Gray", "White", 20 | "Dandelion", "Rose", "Brown mushroom", 21 | "Red mushroom", "Gold", "Iron", 22 | "Double slab", "Slab", "Brick", 23 | "TNT", "Bookshelf", "Mossy rocks", 24 | "Obsidian", 25 | 26 | "Cobblestone slab", "Rope", "Sandstone", 27 | "Snow", "Fire", "Light pink", "Forest green", 28 | "Brown", "Deep blue", "Turquoise", "Ice", 29 | "Ceramic tile", "Magma", "Pillar", "Crate", 30 | "Stone brick" 31 | }; 32 | 33 | cs_bool Block_IsValid(World *world, BlockID id) { 34 | return id < BLOCK_DEFAULT_COUNT || world->info.bdefines[id] != NULL; 35 | } 36 | 37 | BlockID Block_GetFallbackFor(World *world, BlockID id) { 38 | if(world->info.bdefines[id]) 39 | return world->info.bdefines[id]->fallback; 40 | 41 | switch(id) { 42 | case BLOCK_COBBLESLAB: return BLOCK_SLAB; 43 | case BLOCK_ROPE: return BLOCK_BROWN_SHROOM; 44 | case BLOCK_SANDSTONE: return BLOCK_SAND; 45 | case BLOCK_SNOW: return BLOCK_AIR; 46 | case BLOCK_FIRE: return BLOCK_LAVA; 47 | case BLOCK_LIGHTPINK: return BLOCK_PINK; 48 | case BLOCK_FORESTGREEN: return BLOCK_GREEN; 49 | case BLOCK_BROWN: return BLOCK_DIRT; 50 | case BLOCK_DEEPBLUE: return BLOCK_BLUE; 51 | case BLOCK_TURQUOISE: return BLOCK_CYAN; 52 | case BLOCK_ICE: return BLOCK_GLASS; 53 | case BLOCK_CERAMICTILE: return BLOCK_IRON; 54 | case BLOCK_MAGMA: return BLOCK_OBSIDIAN; 55 | case BLOCK_PILLAR: return BLOCK_WHITE; 56 | case BLOCK_CRATE: return BLOCK_WOOD; 57 | case BLOCK_STONEBRICK: return BLOCK_STONE; 58 | default: return id; 59 | } 60 | } 61 | 62 | cs_str Block_GetName(World *world, BlockID id) { 63 | if(!Block_IsValid(world, id)) 64 | return "Unknown block"; 65 | 66 | if(world->info.bdefines[id]) 67 | return world->info.bdefines[id]->name; 68 | 69 | return defaultBlockNames[id]; 70 | } 71 | 72 | BlockID Block_GetIDFor(World *world, BlockDef *bdef) { 73 | for(cs_uint16 i = 0; i < 256; i++) { 74 | if(world->info.bdefines[i] == bdef) 75 | return (BlockID)i; 76 | } 77 | return BLOCK_AIR; 78 | } 79 | 80 | cs_bool Block_Define(World *world, BlockID id, BlockDef *bdef) { 81 | if(id < 1 || world->info.bdefines[id]) return false; 82 | if(Block_GetIDFor(world, bdef) > 0) return false; 83 | bdef->flags &= ~(BDF_UPDATED | BDF_UNDEFINED); 84 | world->info.bdefines[id] = bdef; 85 | return true; 86 | } 87 | 88 | BlockDef *Block_GetDefinition(World *world, BlockID id) { 89 | return world->info.bdefines[id]; 90 | } 91 | 92 | cs_bool Block_Undefine(World *world, BlockDef *bdef) { 93 | BlockID bid = Block_GetIDFor(world, bdef); 94 | if(bid < 1) return false; 95 | for(ClientID id = 0; id < MAX_CLIENTS; id++) { 96 | Client *client = Clients_List[id]; 97 | if(client && Client_IsInWorld(client, world)) 98 | Client_UndefineBlock(client, bid); 99 | } 100 | world->info.bdefines[bid] = NULL; 101 | return true; 102 | } 103 | 104 | void Block_UndefineGlobal(BlockDef *bdef) { 105 | if((bdef->flags & BDF_UNDEFINED) == 0) { 106 | bdef->flags |= BDF_UNDEFINED; 107 | bdef->flags &= ~BDF_UPDATED; 108 | } 109 | } 110 | 111 | void Block_UpdateDefinition(BlockDef *bdef) { 112 | if(bdef->flags & BDF_UPDATED) return; 113 | bdef->flags |= BDF_UPDATED; 114 | 115 | AListField *tmp; 116 | List_Iter(tmp, World_Head) { 117 | World *world = (World *)tmp->value.ptr; 118 | BlockID bid = Block_GetIDFor(world, bdef); 119 | if(bid > BLOCK_AIR) { 120 | if(bdef->flags & BDF_UNDEFINED) { 121 | for(ClientID id = 0; id < MAX_CLIENTS; id++) { 122 | Client *client = Clients_List[id]; 123 | if(client && Client_IsInWorld(client, world)) 124 | Client_UndefineBlock(client, bid); 125 | } 126 | world->info.bdefines[bid] = NULL; 127 | } else { 128 | for(ClientID id = 0; id < MAX_CLIENTS; id++) { 129 | Client *client = Clients_List[id]; 130 | if(client && Client_IsInWorld(client, world)) 131 | Client_DefineBlock(client, bid, bdef); 132 | } 133 | } 134 | } 135 | } 136 | } 137 | 138 | cs_bool Block_BulkUpdateAdd(BulkBlockUpdate *bbu, cs_uint32 offset, BlockID id) { 139 | ((cs_uint32 *)bbu->data.offsets)[bbu->data.count] = htonl(offset); 140 | bbu->data.ids[bbu->data.count++] = id; 141 | 142 | if(bbu->data.count == 255) { 143 | if(bbu->autosend && bbu->world) { 144 | Block_BulkUpdateSend(bbu); 145 | Block_BulkUpdateClean(bbu); 146 | } else return false; 147 | } 148 | 149 | return true; 150 | } 151 | 152 | cs_bool Block_BulkUpdateSend(BulkBlockUpdate *bbu) { 153 | if(!bbu->world) return false; 154 | 155 | for(ClientID cid = 0; cid < MAX_CLIENTS; cid++) { 156 | Client *client = Clients_List[cid]; 157 | if(client && Client_IsInWorld(client, bbu->world)) 158 | Client_BulkBlockUpdate(client, bbu); 159 | } 160 | 161 | return true; 162 | } 163 | 164 | void Block_BulkUpdateClean(BulkBlockUpdate *bbu) { 165 | Memory_Zero(&bbu->data, sizeof(struct _BBUData)); 166 | } 167 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | pull_request: 5 | types: [opened, reopened] 6 | push: 7 | paths: 8 | - src/** 9 | - .github/** 10 | - build.bat 11 | - build 12 | 13 | jobs: 14 | build: 15 | 16 | name: ${{matrix.config.name}} 17 | runs-on: ${{matrix.config.os}}-latest 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | config: 22 | - name: macOS (x64) 23 | os: macos 24 | arch: x86_64 25 | build: ./build 26 | testbin: ./core/out/*/server 27 | 28 | - name: Ubuntu (x64) 29 | os: ubuntu 30 | arch: x86_64 31 | abi: gnu 32 | cfg: | 33 | sudo apt update 34 | sudo apt install -y zlib1g-dev 35 | build: ./build 36 | testbin: ./core/out/x86_64*/server 37 | 38 | - name: Ubuntu (x86) 39 | os: ubuntu 40 | arch: i686 41 | abi: gnu 42 | cfg: | 43 | sudo dpkg --add-architecture i386 44 | sudo apt update 45 | sudo apt install -y gcc-i686-linux-gnu zlib1g-dev:i386 46 | build: ./build 47 | testbin: ./core/out/i686*/server 48 | 49 | - name: Ubuntu (arm64) 50 | os: ubuntu 51 | arch: aarch64 52 | abi: gnu 53 | hostcc: x86_64-linux-gnu-gcc 54 | cfg: | 55 | sudo apt update 56 | sudo apt install -y gcc-aarch64-linux-gnu 57 | build: ./build 58 | 59 | - name: Ubuntu (armv7hf) 60 | os: ubuntu 61 | arch: arm 62 | abi: gnueabihf 63 | hostcc: i686-linux-gnu-gcc 64 | cfg: | 65 | sudo apt update 66 | sudo apt install -y gcc-arm-linux-gnueabihf gcc-i686-linux-gnu 67 | build: ./build 68 | 69 | - name: Windows (x64) 70 | os: windows 71 | arch: x86_64 72 | build: .\build.bat 73 | testbin: .\core\out\x64\server.exe 74 | 75 | - name: Windows (x86) 76 | os: windows 77 | arch: x86 78 | build: .\build.bat 79 | testbin: .\core\out\x86\server.exe 80 | 81 | steps: 82 | - name: Checkout Core repo 83 | uses: actions/checkout@v3 84 | with: 85 | fetch-depth: 0 86 | path: core 87 | 88 | - name: Checkout Base plugin 89 | uses: actions/checkout@v3 90 | with: 91 | repository: igor725/cs-base 92 | path: cs-base 93 | 94 | - name: Checkout Survival plugin 95 | uses: actions/checkout@v3 96 | with: 97 | repository: igor725/cs-survival 98 | path: cs-survival 99 | 100 | - name: Checkout Lua plugin 101 | uses: actions/checkout@v3 102 | with: 103 | repository: igor725/cs-lua 104 | path: cs-lua 105 | 106 | - name: Checkout WorldEdit plugin 107 | uses: actions/checkout@v3 108 | with: 109 | repository: igor725/cs-worldedit 110 | path: cs-worldedit 111 | 112 | - name: Checkout Web plugin 113 | uses: actions/checkout@v3 114 | with: 115 | repository: igor725/cs-web 116 | path: cs-web 117 | 118 | - name: Detect MSVC (Windows) 119 | uses: ilammy/msvc-dev-cmd@v1 120 | if: ${{matrix.config.os == 'windows'}} 121 | with: 122 | arch: ${{matrix.config.arch}} 123 | 124 | - name: Prebuild preparations 125 | if: ${{matrix.config.cfg != null}} 126 | run: ${{matrix.config.cfg}} 127 | 128 | - name: Set environment (Linux) 129 | if: ${{matrix.config.os == 'ubuntu'}} 130 | run: | 131 | echo "CSERVER_BUILD_TARGET=${{matrix.config.arch}}-linux-${{matrix.config.abi}}" >> $GITHUB_ENV 132 | 133 | - name: Build core 134 | working-directory: core 135 | run: | 136 | ${{matrix.config.build}} wall noprompt 137 | 138 | - name: Build plugins 139 | working-directory: core 140 | env: 141 | HOSTCC: ${{matrix.config.hostcc}} 142 | MACOSX_DEPLOYMENT_TARGET: "11.0" 143 | run: | 144 | ${{matrix.config.build}} wall pb base install && 145 | ${{matrix.config.build}} wall pb survival install && 146 | ${{matrix.config.build}} wall pb worldedit install && 147 | ${{matrix.config.build}} wall pb lua install && 148 | ${{matrix.config.build}} wall pb web install wbuild 149 | 150 | - name: Make some tests 151 | if: ${{matrix.config.testbin != null}} 152 | run: ${{matrix.config.testbin}} testmode 153 | 154 | - name: Collect changes (Linux) 155 | if: ${{matrix.config.os != 'windows'}} 156 | working-directory: core 157 | run: | 158 | git log --format=medium $(git describe --tags --abbrev=0 @^)..HEAD > out/CHANGES 159 | 160 | - name: Collect changes (Windows) 161 | if: ${{matrix.config.os == 'windows'}} 162 | working-directory: core 163 | run: | 164 | $lasttag = (git describe --tags --abbrev=0 @^) -join "`n" 165 | Start-Process -NoNewWindow -RedirectStandardOutput out\CHANGES git.exe "log --format=medium $lasttag..HEAD" 166 | 167 | - name: Upload artifacts 168 | uses: actions/upload-artifact@v3 169 | with: 170 | name: cserver-${{matrix.config.os}}-${{matrix.config.arch}} 171 | path: | 172 | core/out 173 | !core/out/**/objs 174 | !core/out/**/worlds 175 | !core/out/**/configs 176 | !core/out/**/*.ilk 177 | !core/out/**/*.exp 178 | -------------------------------------------------------------------------------- /src/platforms/shared.c: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | #include "platform.h" 3 | #include "cserror.h" 4 | #include "str.h" 5 | 6 | void *Memory_Alloc(cs_size num, cs_size size) { 7 | void *ptr = Memory_TryAlloc(num, size); 8 | if(!ptr) Error_PrintSys(true); 9 | return ptr; 10 | } 11 | 12 | void *Memory_Realloc(void *oldptr, cs_size new) { 13 | void *newptr = Memory_TryRealloc(oldptr, new); 14 | if(!newptr) Error_PrintSys(true); 15 | return newptr; 16 | } 17 | 18 | void Memory_Copy(void *dst, const void *src, cs_size count) { 19 | cs_byte *u8dst = (cs_byte *)dst, 20 | *u8src = (cs_byte *)src; 21 | while(count--) *u8dst++ = *u8src++; 22 | } 23 | 24 | void Memory_Fill(void *dst, cs_size count, cs_byte val) { 25 | cs_byte *u8dst = (cs_byte *)dst; 26 | while(count--) *u8dst++ = val; 27 | } 28 | 29 | cs_bool Memory_Compare(const cs_byte *src1, const cs_byte *src2, cs_size len) { 30 | if(!src1 || !src2) return false; 31 | 32 | while(len--) 33 | if(*src1++ != *src2++) return false; 34 | 35 | return true; 36 | } 37 | 38 | cs_file File_Open(cs_str path, cs_str mode) { 39 | return fopen(path, mode); 40 | } 41 | 42 | cs_size File_Read(void *ptr, cs_size size, cs_size count, cs_file fp) { 43 | return fread(ptr, size, count, fp); 44 | } 45 | 46 | cs_int32 File_ReadLine(cs_file fp, cs_char *line, cs_size len) { 47 | cs_char *sym; 48 | 49 | for(sym = line; (cs_size)(sym - line) < len; sym++) { 50 | cs_int32 ch = File_GetChar(fp); 51 | if(ch == EOF || ch == '\n') { 52 | *sym = '\0'; 53 | return (cs_int32)(sym - line); 54 | } else if(ch != '\r') 55 | *sym = (cs_char)ch; 56 | } 57 | 58 | *sym = '\0'; 59 | return -1; 60 | } 61 | 62 | cs_size File_Write(const void *ptr, cs_size size, cs_size count, cs_file fp) { 63 | return fwrite(ptr, size, count, fp); 64 | } 65 | 66 | cs_int32 File_GetChar(cs_file fp) { 67 | return fgetc(fp); 68 | } 69 | 70 | cs_int32 File_Error(cs_file fp) { 71 | return ferror(fp); 72 | } 73 | 74 | cs_bool File_IsEnd(cs_file fp) { 75 | return feof(fp) > 0; 76 | } 77 | 78 | cs_int32 File_WriteFormat(cs_file fp, cs_str fmt, ...) { 79 | va_list args; 80 | va_start(args, fmt); 81 | cs_int32 len = vfprintf(fp, fmt, args); 82 | va_end(args); 83 | 84 | return len; 85 | } 86 | 87 | cs_bool File_Flush(cs_file fp) { 88 | return fflush(fp) == 0; 89 | } 90 | 91 | cs_long File_Seek(cs_file fp, cs_long offset, cs_int32 origin) { 92 | if(fseek(fp, offset, origin) != 0) return -1; 93 | return ftell(fp); 94 | } 95 | 96 | cs_bool File_Close(cs_file fp) { 97 | return fclose(fp) != 0; 98 | } 99 | 100 | static const struct _subnet { 101 | cs_ulong net; 102 | cs_ulong mask; 103 | } localnets[] = { 104 | {0x0000007f, 0x000000FF}, 105 | {0x0000000A, 0x000000FF}, 106 | {0x000010AC, 0x00000FFF}, 107 | {0x0000A8C0, 0x0000FFFF}, 108 | 109 | {0x00000000, 0x00000000} 110 | }; 111 | 112 | cs_bool Socket_IsLocal(cs_ulong addr) { 113 | for(const struct _subnet *s = localnets; s->mask; s++) { 114 | if((addr & s->mask) == s->net) 115 | return true; 116 | } 117 | 118 | return false; 119 | } 120 | 121 | cs_bool Socket_SetAddrGuess(struct sockaddr_in *ssa, cs_str host, cs_uint16 port) { 122 | cs_int32 ret; 123 | 124 | if((ret = Socket_SetAddr(ssa, host, port)) == 0) { 125 | struct addrinfo *addr; 126 | struct addrinfo hints = { 127 | .ai_family = AF_INET, 128 | .ai_socktype = 0, 129 | .ai_protocol = 0 130 | }; 131 | 132 | cs_char strport[6]; 133 | String_FormatBuf(strport, 6, "%d", port); 134 | if((ret = getaddrinfo(host, strport, &hints, &addr)) == 0) { 135 | *ssa = *(struct sockaddr_in *)addr->ai_addr; 136 | freeaddrinfo(addr); 137 | return true; 138 | } 139 | } 140 | 141 | return ret == 1; 142 | } 143 | 144 | cs_bool Socket_Bind(Socket sock, struct sockaddr_in *addr) { 145 | #if defined(CORE_USE_UNIX) 146 | if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &(cs_int32){1}, 4) < 0) { 147 | return false; 148 | } 149 | #elif defined(CORE_USE_WINDOWS) 150 | if(setsockopt(sock, SOL_SOCKET, SO_DONTLINGER, (void*)&(cs_int32){0}, 4) < 0) { 151 | return false; 152 | } 153 | #endif 154 | 155 | if(bind(sock, (const struct sockaddr *)addr, sizeof(struct sockaddr_in)) < 0) { 156 | return false; 157 | } 158 | 159 | if(listen(sock, SOMAXCONN) < 0) { 160 | return false; 161 | } 162 | 163 | return true; 164 | } 165 | 166 | cs_bool Socket_Connect(Socket sock, struct sockaddr_in *addr) { 167 | return connect(sock, (struct sockaddr *)addr, sizeof(struct sockaddr_in)) == 0; 168 | } 169 | 170 | Socket Socket_Accept(Socket sock, struct sockaddr_in *addr) { 171 | return accept(sock, (struct sockaddr *)addr, &(socklen_t){sizeof(struct sockaddr_in)}); 172 | } 173 | 174 | cs_int32 Socket_Receive(Socket sock, cs_char *buf, cs_int32 len, cs_int32 flags) { 175 | return recv(sock, buf, len, MSG_NOSIGNAL | MSG_DONTWAIT | flags); 176 | } 177 | 178 | cs_int32 Socket_Send(Socket sock, const cs_char *buf, cs_int32 len) { 179 | return (cs_int32)send(sock, buf, len, MSG_NOSIGNAL); 180 | } 181 | 182 | cs_bool Socket_Shutdown(Socket sock, cs_int32 how) { 183 | return shutdown(sock, how) == 0; 184 | } 185 | 186 | cs_bool Directory_Ensure(cs_str path) { 187 | return Directory_Exists(path) || Directory_Create(path); 188 | } 189 | 190 | cs_bool DLib_LoadAll(cs_str const lib[], cs_str const symlist[], void **ctx) { 191 | ctx[0] = NULL; 192 | while(*lib && !DLib_Load(*lib++, ctx)); 193 | 194 | for(cs_int32 i = 0; ctx[0] && symlist[i]; i++) 195 | if(!DLib_GetSym(ctx[0], symlist[i], &ctx[i + 1])) { 196 | DLib_Unload(ctx[0]); 197 | ctx[0] = NULL; 198 | } 199 | 200 | return ctx[0] != NULL; 201 | } 202 | 203 | cs_error Thread_GetError(void) { 204 | return errno; 205 | } 206 | -------------------------------------------------------------------------------- /src/strstor.c: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | #include "str.h" 3 | #include "list.h" 4 | #include "platform.h" 5 | #include "strstor.h" 6 | 7 | static KListField *storage = NULL; 8 | 9 | cs_bool Sstor_IsExists(cs_str key) { 10 | KListField *tmp; 11 | List_Iter(tmp, storage) { 12 | if(String_CaselessCompare(tmp->key.str, key)) 13 | return true; 14 | } 15 | return false; 16 | } 17 | 18 | cs_str Sstor_Get(cs_str key) { 19 | KListField *tmp; 20 | List_Iter(tmp, storage) { 21 | if(String_CaselessCompare(tmp->key.str, key)) 22 | return tmp->value.str; 23 | } 24 | return NULL; 25 | } 26 | 27 | cs_bool Sstor_Set(cs_str key, cs_str value) { 28 | if(String_Length(key) < 2) return false; 29 | if(Sstor_IsExists(key)) return false; 30 | cs_str _key = String_AllocCopy(key), 31 | _value = String_AllocCopy(value); 32 | return KList_AddField(&storage, (void *)_key, (void *)_value) != NULL; 33 | } 34 | 35 | cs_bool Strstro_Remove(cs_str key) { 36 | KListField *tmp; 37 | List_Iter(tmp, storage) { 38 | if(String_CaselessCompare(tmp->key.str, key)) { 39 | Memory_Free(tmp->key.ptr); 40 | Memory_Free(tmp->value.ptr); 41 | KList_Remove(&storage, tmp); 42 | return true; 43 | } 44 | } 45 | return false; 46 | } 47 | 48 | void Sstor_Cleanup(void) { 49 | KListField *tmp, *prev = NULL; 50 | List_Iter(tmp, storage) { 51 | if(prev) KList_Remove(&storage, prev); 52 | Memory_Free(tmp->key.ptr); 53 | Memory_Free(tmp->value.ptr); 54 | prev = tmp; 55 | } 56 | if(prev) KList_Remove(&storage, prev); 57 | } 58 | 59 | cs_bool Sstor_Defaults(void) { 60 | Sstor_Set("WGEN_ERROR", "Oh! Error happened in the world generator for \"%s\""); 61 | Sstor_Set("WGEN_INVDIM", "Invalid dimensions specified for \"%s\""); 62 | Sstor_Set("WGEN_NOGEN", "Invalid generator specified for \"%s\""); 63 | 64 | Sstor_Set("SV_START", "Server started on %s:%d"); 65 | Sstor_Set("SV_BIND_FAIL", "Failed to bind %s:%d"); 66 | Sstor_Set("SV_NOWORLDS", "No worlds loaded"); 67 | Sstor_Set("SV_WPARSE_ERR", "Failed to parse worlds-list: string too long"); 68 | Sstor_Set("SV_WLOAD_ERR", "Failed to %s world %s: (%d, %d)"); 69 | Sstor_Set("SV_CFGL_ERR", "Failed to parse line %d from %s: (%s, %s)"); 70 | Sstor_Set("SV_CFG_ERR", "Failed to %s %s: (%s, %s)"); 71 | Sstor_Set("SV_CFG_ERR2", "Failed to %s %s: (%s, %d)"); 72 | Sstor_Set("SV_WLDONE", "%d world(-s) successfully loaded"); 73 | Sstor_Set("SV_STOPNOTE", "Press Ctrl+C to stop the server"); 74 | Sstor_Set("SV_BADTICK_BW", "Time ran backwards? Time between the last two ticks < 0ms"); 75 | Sstor_Set("SV_BADTICK", "Last server tick took %dms!"); 76 | Sstor_Set("SV_STOP_PL", "Kicking players..."); 77 | Sstor_Set("SV_STOP_SW", "Saving worlds..."); 78 | Sstor_Set("SV_STOP_SC", "Saving server config..."); 79 | Sstor_Set("SV_STOP_UP", "Unloading plugins..."); 80 | 81 | Sstor_Set("CMD_UNK", "Unknown command, type \"/help [page]\" for more info"); 82 | Sstor_Set("CMD_NOCON", "This command cannot be called from the console"); 83 | Sstor_Set("CMD_NOPERM", "Access denied"); 84 | 85 | Sstor_Set("KICK_NOREASON", "Kicked without reason"); 86 | Sstor_Set("KICK_PROTOVER", "Invalid protocol version (e: 0x%02X, g: 0x%02X)"); 87 | Sstor_Set("KICK_NAMECHARS", "Your name contains prohibited characters"); 88 | Sstor_Set("KICK_NAMEINUSE", "This name is already in use"); 89 | Sstor_Set("KICK_AUTHFAIL", "Authorization failed"); 90 | Sstor_Set("KICK_NERR", "Network error %d"); 91 | Sstor_Set("KICK_MANYCONN", "Too many connections from one IP"); 92 | Sstor_Set("KICK_REJ", "Server rejected your connection attempt"); 93 | Sstor_Set("KICK_FULL", "Server is full"); 94 | Sstor_Set("KICK_TIMEOUT", "Timed out"); 95 | Sstor_Set("KICK_PACKETSPAM", "Too many packets per second"); 96 | Sstor_Set("KICK_PERR_UNEXP", "Failed to handle packet 0x%02X"); 97 | Sstor_Set("KICK_PERR_NOHANDLER", "Unknown opcode 0x%02X"); 98 | Sstor_Set("KICK_UNKBID", "Block with ID %d is not registred for this world"); 99 | Sstor_Set("KICK_ZERR", "Compression error: %.45s"); 100 | Sstor_Set("KICK_INT", "Internal server error"); 101 | Sstor_Set("KICK_STOP", "Server stopped"); 102 | 103 | Sstor_Set("HBEAT_URL", "Server play URL: %s"); 104 | Sstor_Set("HBEAT_ERR", "Heartbeat error: %s"); 105 | Sstor_Set("HBEAT_ERR_ER", "Empty server response"); 106 | Sstor_Set("HBEAT_ERR_HF", "HTTP request failed"); 107 | Sstor_Set("HBEAT_ERR_CF", "Failed to open HTTP connection"); 108 | Sstor_Set("HBEAT_KEYCHECK_ERR", "VanillaKeyChecker: MD5_Start() returned false, can't check user key validity"); 109 | Sstor_Set("HBEAT_SECRET_COMM1", "#Remove this file if you want to generate new secret key\n"); 110 | Sstor_Set("HBEAT_SECRET_COMM2", "#This key used by the heartbeat as server's \"salt\" for user authentication check\n"); 111 | 112 | Sstor_Set("PLUG_DEPR_API", "Please upgrade your server software. Plugin \"%s\" compiled for PluginAPI v%03d, but server uses v%d."); 113 | Sstor_Set("PLUG_DEPR", "Plugin \"%s\" is deprecated. The server uses PluginAPI v%03d, but this plugin is compiled for v%03d."); 114 | Sstor_Set("PLUG_DEPR2", "Plugin \"%s\" is deprecated. The server uses PluginAPI v%03d, but this plugin is compiled for v%03d. You may find its updated version here: %s."); 115 | Sstor_Set("PLUG_ERROR", "Failed to load plugin %s: Plugin_Load returned false"); 116 | Sstor_Set("PLUG_ITFS", "Failed to load plugin %s: Interface collision detected"); 117 | 118 | Sstor_Set("Z_NOGZ", "Your zlib installation has no gzip support"); 119 | Sstor_Set("Z_LVL1", "Your zlib installation supports only one, lowest compression level!"); 120 | Sstor_Set("Z_LVL2", "This means less CPU load in deflate tasks, but the worlds will take much more space on your disk"); 121 | Sstor_Set("Z_LVL3", "It also means a longer connection of players to the server"); 122 | 123 | return true; 124 | } 125 | -------------------------------------------------------------------------------- /src/http.c: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | #include "platform.h" 3 | #include "str.h" 4 | #include "http.h" 5 | 6 | #if defined(HTTP_USE_WININET_BACKEND) 7 | static cs_str wisymlist[] = { 8 | "InternetOpenA", "InternetConnectA", 9 | "HttpOpenRequestA", "HttpSendRequestA", 10 | "InternetReadFile", "InternetCloseHandle", 11 | NULL 12 | }; 13 | 14 | struct _WinInet { 15 | void *lib; 16 | 17 | HINTERNET(__stdcall *IOpen)(cs_str, cs_ulong, cs_str, cs_str, cs_ulong); 18 | HINTERNET(__stdcall *IConnect)(HINTERNET, cs_str, INTERNET_PORT, cs_str, cs_str, cs_ulong, cs_ulong, cs_uintptr); 19 | HINTERNET(__stdcall *IOpenRequest)(HINTERNET, cs_str, cs_str, cs_str, cs_str, cs_str *, cs_ulong, cs_uintptr); 20 | BOOL(__stdcall *HSendRequest)(HINTERNET, cs_str, cs_ulong, void *, cs_ulong); 21 | BOOL(__stdcall *IReadFile)(HINTERNET, void *, cs_ulong, cs_ulong *); 22 | BOOL(__stdcall *IClose)(HINTERNET); 23 | } WinInet; 24 | 25 | static HINTERNET hInternet = NULL; 26 | 27 | INL static cs_bool InitBackend(void) { 28 | if(!WinInet.lib && !DLib_LoadAll(DLib_List("wininet.dll"), wisymlist, (void **)&WinInet)) 29 | return false; 30 | 31 | return (hInternet = WinInet.IOpen(HTTP_USERAGENT, 32 | INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0 33 | )) != NULL; 34 | } 35 | 36 | void Http_Uninit(void) { 37 | if(!WinInet.lib) return; 38 | if(hInternet) { 39 | WinInet.IClose(hInternet); 40 | hInternet = NULL; 41 | } 42 | DLib_Unload(WinInet.lib); 43 | Memory_Zero(&WinInet, sizeof(WinInet)); 44 | } 45 | 46 | cs_bool Http_Open(Http *http, cs_str domain) { 47 | if(!hInternet && !InitBackend()) return false; 48 | http->conn = WinInet.IConnect( 49 | hInternet, domain, 50 | http->secure ? 443 : 80, 51 | NULL, NULL, INTERNET_SERVICE_HTTP, 0, 1 52 | ); 53 | return http->conn != (HINTERNET)-1; 54 | } 55 | 56 | cs_bool Http_Request(Http *http, cs_str url) { 57 | if(!hInternet) return false; 58 | http->req = WinInet.IOpenRequest( 59 | http->conn, "GET", url, 60 | NULL, NULL, 0, http->secure ? INTERNET_FLAG_SECURE : 0, 1 61 | ); 62 | if(!http->req) return false; 63 | return WinInet.HSendRequest(http->req, NULL, 0, NULL, 0) == true; 64 | } 65 | 66 | cs_ulong Http_ReadResponse(Http *http, cs_char *buf, cs_ulong sz) { 67 | cs_ulong readed = 0L; 68 | if(hInternet && WinInet.IReadFile(http->req, buf, sz - 1, &readed)) 69 | buf[readed] = '\0'; 70 | return readed; 71 | } 72 | 73 | void Http_Cleanup(Http *http) { 74 | if(!hInternet) return; 75 | if(http->conn) WinInet.IClose(http->conn); 76 | if(http->req) WinInet.IClose(http->req); 77 | } 78 | #elif defined(HTTP_USE_CURL_BACKEND) 79 | static cs_str csymlist[] = { 80 | "curl_easy_init", "curl_easy_setopt", 81 | "curl_easy_perform", "curl_easy_cleanup", 82 | "curl_global_cleanup", NULL 83 | }; 84 | 85 | static cs_str libcurl[] = { 86 | #if defined(CORE_USE_UNIX) 87 | "libcurl." DLIB_EXT ".4", 88 | "libcurl." DLIB_EXT ".3", 89 | "libcurl." DLIB_EXT, 90 | #elif defined(CORE_USE_WINDOWS) 91 | "curl.dll", 92 | "libcurl.dll", 93 | # ifdef _WIN64 94 | "libcurl-x64.dll", 95 | # endif 96 | #else 97 | # error This file wants to be hacked 98 | #endif 99 | NULL 100 | }; 101 | 102 | static struct _cURLLib { 103 | void *lib; 104 | 105 | CURL *(*easy_init)(void); 106 | cs_int32 (*easy_setopt)(CURL *, cs_int32, ...); 107 | cs_int32 (*easy_perform)(CURL *); 108 | void (*easy_cleanup)(CURL *); 109 | 110 | void (*global_cleanup)(void); 111 | } curl; 112 | 113 | void Http_Uninit(void) { 114 | if(!curl.lib) return; 115 | curl.global_cleanup(); 116 | DLib_Unload(curl.lib); 117 | Memory_Zero(&curl, sizeof(curl)); 118 | } 119 | 120 | cs_bool Http_Open(Http *http, cs_str domain) { 121 | if(!curl.lib && !DLib_LoadAll(libcurl, csymlist, (void **)&curl)) 122 | return false; 123 | http->domain = String_AllocCopy(domain); 124 | http->handle = curl.easy_init(); 125 | if(http->handle) { 126 | curl.easy_setopt(http->handle, CURLOPT_USERAGENT, HTTP_USERAGENT); 127 | curl.easy_setopt(http->handle, CURLOPT_FOLLOWLOCATION, 1L); 128 | return true; 129 | } 130 | return false; 131 | } 132 | 133 | cs_bool Http_Request(Http *http, cs_str url) { 134 | if(!curl.lib || !http->domain || !http->handle) return false; 135 | http->path = (cs_char *)(http->secure ? String_AllocCopy("https://") : String_AllocCopy("http://")); 136 | cs_size memsize = 0; 137 | http->path = String_Grow(http->path, String_Length(http->domain) + String_Length(url), &memsize); 138 | String_Append(http->path, memsize, http->domain); 139 | String_Append(http->path, memsize, url); 140 | return curl.easy_setopt(http->handle, CURLOPT_URL, http->path) == CURLE_OK; 141 | } 142 | 143 | static cs_size writefunc(cs_char *ptr, cs_size sz, cs_size num, void *ud) { 144 | Http *http = (Http *)ud; 145 | cs_size full = sz * num, 146 | newlen = min(http->rsplen + full, http->buflen); 147 | 148 | if(http->rsplen < http->buflen) { 149 | Memory_Copy(http->buf + http->rsplen, ptr, newlen - http->rsplen - 1); 150 | http->rsplen = newlen; 151 | } 152 | 153 | return full; 154 | } 155 | 156 | cs_ulong Http_ReadResponse(Http *http, cs_char *buf, cs_ulong sz) { 157 | if(!curl.lib || !http->handle) return false; 158 | http->buf = buf, http->buflen = sz; 159 | curl.easy_setopt(http->handle, CURLOPT_WRITEFUNCTION, writefunc); 160 | curl.easy_setopt(http->handle, CURLOPT_WRITEDATA, (void *)http); 161 | if(curl.easy_perform(http->handle) == CURLE_OK) 162 | return (cs_ulong)http->rsplen; 163 | return 0L; 164 | } 165 | 166 | void Http_Cleanup(Http *http) { 167 | if(!curl.lib) return; 168 | if(http->domain) Memory_Free((void *)http->domain); 169 | if(http->path) Memory_Free((void *)http->path); 170 | if(http->handle) curl.easy_cleanup(http->handle); 171 | Memory_Zero(http, sizeof(Http)); 172 | } 173 | #else 174 | # error No HTTP backend selected! 175 | #endif 176 | -------------------------------------------------------------------------------- /src/client.h: -------------------------------------------------------------------------------- 1 | #ifndef CLIENT_H 2 | #define CLIENT_H 3 | #include "core.h" 4 | #include "vector.h" 5 | #include "types/platform.h" 6 | #include "types/world.h" 7 | #include "types/block.h" 8 | #include "types/cpe.h" 9 | #include "types/groups.h" 10 | #include "types/client.h" 11 | #include "types/keys.h" 12 | 13 | #ifndef CORE_BUILD_PLUGIN 14 | void Client_Init(Client *client, Socket fd, cs_ulong addr); 15 | 16 | void Client_Tick(Client *client); 17 | void Client_Free(Client *client); 18 | 19 | NOINL cs_bool Client_DefineBlock(Client *client, BlockID id, BlockDef *block); 20 | NOINL cs_bool Client_UndefineBlock(Client *client, BlockID id); 21 | #endif 22 | 23 | 24 | API void Client_BulkBlockUpdate(Client *client, BulkBlockUpdate *bbu); 25 | 26 | API cs_byte Clients_GetCount(EClientState state); 27 | 28 | API Client *Client_NewBot(void); 29 | API cs_bool Client_IsBot(Client *client); 30 | 31 | API void Client_Lock(Client *client); 32 | API void Client_Unlock(Client *client); 33 | 34 | API cs_char *Client_StartRaw(Client *client, cs_uint32 msize); 35 | API cs_bool Client_EndRaw(Client *client, cs_uint32 psize); 36 | 37 | API cs_bool Client_ChangeWorld(Client *client, World *world); 38 | API void Client_Chat(Client *client, EMesgType type, cs_str message); 39 | API void Client_Kick(Client *client, cs_str reason); 40 | API void Client_KickFormat(Client *client, cs_str fmtreason, ...); 41 | API void Client_UpdateWorldInfo(Client *client, World *world, cs_bool updateAll); 42 | API cs_bool Client_Update(Client *client); 43 | API cs_bool Client_SendHacks(Client *client, CPEHacks *hacks); 44 | API CPECuboid *Client_NewSelection(Client *client); 45 | API cs_bool Client_UpdateSelection(Client *client, CPECuboid *cub); 46 | API cs_bool Client_RemoveSelection(Client *client, CPECuboid *cub); 47 | API cs_bool Client_TeleportTo(Client *client, Vec *pos, Ang *ang); 48 | API cs_bool Client_TeleportToSpawn(Client *client); 49 | API cs_bool Client_CheckState(Client *client, EClientState state); 50 | 51 | API cs_bool Client_IsClosed(Client *client); 52 | API cs_bool Client_IsLocal(Client *client); 53 | API cs_bool Client_IsInSameWorld(Client *client, Client *other); 54 | API cs_bool Client_IsInWorld(Client *client, World *world); 55 | API cs_bool Client_IsOP(Client *client); 56 | API cs_bool Client_IsSpawned(Client *client); 57 | API cs_bool Client_IsFirstSpawn(Client *client); 58 | 59 | API cs_bool Client_SetDisplayName(Client *client, cs_str name); 60 | API cs_bool Client_SetWeather(Client *client, cs_int8 type); 61 | API cs_bool Client_SetInvOrder(Client *client, cs_byte order, BlockID block); 62 | API cs_bool Client_SetServerIdent(Client *client, cs_str name, cs_str motd); 63 | API cs_bool Client_SetEnvProperty(Client *client, EProp property, cs_int32 value); 64 | API cs_bool Client_SetEnvColor(Client *client, EColor type, Color3* color); 65 | API cs_bool Client_SetTexturePack(Client *client, cs_str url); 66 | API cs_bool Client_AddTextColor(Client *client, Color4* color, cs_char code); 67 | API void Client_SetBlock(Client *client, SVec *pos, BlockID id); 68 | API cs_bool Client_SetModel(Client *client, cs_int16 model); 69 | API cs_bool Client_SetModelStr(Client *client, cs_str model); 70 | API cs_bool Client_SetBlockPerm(Client *client, BlockID block, cs_bool allowPlace, cs_bool allowDestroy); 71 | API cs_bool Client_SetHeldBlock(Client *client, BlockID block, cs_bool preventChange); 72 | API cs_bool Client_SetClickDistance(Client *client, cs_uint16 dist); 73 | API cs_bool Client_SetHotkey(Client *client, cs_str action, ELWJGLKey keycode, ELWJGLMod keymod); 74 | API cs_bool Client_SetHotbar(Client *client, cs_byte pos, BlockID block); 75 | API cs_bool Client_SetSkin(Client *client, cs_str skin); 76 | API cs_bool Client_SetSpawn(Client *client, Vec *pos, Ang *ang); 77 | API cs_bool Client_SetOP(Client *client, cs_bool state); 78 | API cs_bool Client_SetVelocity(Client *client, Vec *velocity, cs_byte mode); 79 | API cs_bool Client_SetProp(Client *client, EEntProp prop, cs_int32 value); 80 | API cs_bool Client_SetGroup(Client *client, cs_uintptr gid); 81 | API cs_bool Client_SpawnParticle(Client *client, cs_byte id, Vec *pos, Vec *origin); 82 | API cs_bool Client_SendPluginMessage(Client *client, cs_byte channel, cs_str message); 83 | API cs_bool Client_UndefineModel(Client *client, cs_byte id); 84 | API cs_bool Client_ExtTeleportTo(Client *client, cs_byte behavior, Vec *pos, Ang *ang); 85 | API cs_bool Client_SetLighting(Client *client, ELightMode mode, cs_bool locked); 86 | 87 | API EClientState Client_GetState(Client *client); 88 | API cs_str Client_GetName(Client *client); 89 | API cs_str Client_GetDisplayName(Client *client); 90 | API cs_str Client_GetAppName(Client *client); 91 | API cs_str Client_GetKey(Client *client); 92 | API cs_str Client_GetSkin(Client *client); 93 | API cs_str Client_GetDisconnectReason(Client *client); 94 | API ClientID Client_GetID(Client *client); 95 | API Client *Client_GetByID(ClientID id); 96 | API Client *Client_GetByName(cs_str name); 97 | API World *Client_GetWorld(Client *client); 98 | API BlockID Client_GetStandBlock(Client *client); 99 | API cs_int8 Client_GetFluidLevel(Client *client, BlockID *fluid); 100 | API cs_int16 Client_GetModel(Client *client); 101 | API BlockID Client_GetHeldBlock(Client *client); 102 | API cs_uint16 Client_GetClickDistance(Client *client); 103 | API cs_float Client_GetClickDistanceInBlocks(Client *client); 104 | API void Client_GetPosition(Client *client, Vec *pos, Ang *ang); 105 | API cs_int32 Client_GetExtVer(Client *client, cs_ulong exthash); 106 | API cs_uint32 Client_GetAddr(Client *client); 107 | API cs_int32 Client_GetPing(Client *client); 108 | API cs_float Client_GetAvgPing(Client *client); 109 | API CGroup *Client_GetGroup(Client *client); 110 | API cs_uintptr Client_GetGroupID(Client *client); 111 | 112 | API cs_bool Client_Spawn(Client *client); 113 | API cs_bool Client_Despawn(Client *client); 114 | 115 | VAR Client *Clients_List[MAX_CLIENTS]; 116 | #endif // CLIENT_H 117 | -------------------------------------------------------------------------------- /src/protocol.h: -------------------------------------------------------------------------------- 1 | #ifndef PROTOCOL_H 2 | #define PROTOCOL_H 3 | #include "core.h" 4 | #include "types/cpe.h" 5 | #include "types/client.h" 6 | #include "types/protocol.h" 7 | #include "types/block.h" 8 | #include "types/keys.h" 9 | 10 | typedef cs_bool(*packetHandler)(Client *, cs_char *); 11 | 12 | #ifndef CORE_BUILD_PLUGIN 13 | Packet *Packet_Get(EPacketID id); 14 | void Packet_UnregisterAll(void); 15 | void Packet_RegisterDefault(void); 16 | 17 | /* 18 | * Врайтеры и хендлеры 19 | * ванильного протокола 20 | */ 21 | 22 | NOINL void Vanilla_WriteServerIdent(Client *client, cs_str name, cs_str motd); 23 | NOINL void Vanilla_WriteLvlInit(Client *client); 24 | NOINL void Vanilla_WriteLvlFin(Client *client, SVec *dims); 25 | NOINL void Vanilla_WriteSetBlock(Client *client, SVec *pos, BlockID block); 26 | NOINL void Vanilla_WriteSpawn(Client *client, Client *other); 27 | NOINL void Vanilla_WriteTeleport(Client *client, Vec *pos, Ang *ang); 28 | NOINL void Vanilla_WritePosAndOrient(Client *client, Client *other); 29 | NOINL void Vanilla_WriteDespawn(Client *client, Client *other); 30 | NOINL void Vanilla_WriteChat(Client *client, EMesgType type, cs_str mesg); 31 | NOINL void Vanilla_WriteKick(Client *client, cs_str reason); 32 | NOINL void Vanilla_WriteUserType(Client *client, cs_byte type); 33 | 34 | /* 35 | * Врайтеры и хендлеры 36 | * CPE протокола и прочие, 37 | * связанные с CPE вещи 38 | */ 39 | 40 | NOINL void CPE_WriteInfo(Client *client); 41 | NOINL void CPE_WriteExtEntry(Client *client, CPESvExt *ext); 42 | NOINL void CPE_WriteClickDistance(Client *client, cs_uint16 dist); 43 | NOINL void CPE_CustomBlockSupportLevel(Client *client, cs_byte level); 44 | NOINL void CPE_WriteInventoryOrder(Client *client, cs_byte order, BlockID block); 45 | NOINL void CPE_WriteHoldThis(Client *client, BlockID block, cs_bool preventChange); 46 | NOINL void CPE_WriteSetHotKey(Client *client, cs_str action, ELWJGLKey keycode, ELWJGLMod keymod); 47 | NOINL void CPE_WriteAddName(Client *client, Client *other); 48 | NOINL void CPE_WriteAddEntity(Client *client, cs_int32 ver, Client *other); 49 | NOINL void CPE_WriteRemoveName(Client *client, Client *other); 50 | NOINL void CPE_WriteEnvColor(Client *client, cs_byte type, Color3* col); 51 | NOINL void CPE_WriteMakeSelection(Client *client, CPECuboid *cub); 52 | NOINL void CPE_WriteRemoveSelection(Client *client, cs_byte id); 53 | NOINL void CPE_WriteHackControl(Client *client, CPEHacks *hacks); 54 | NOINL void CPE_WriteDefineBlock(Client *client, BlockID id, BlockDef *block); 55 | NOINL void CPE_WriteUndefineBlock(Client *client, BlockID id); 56 | NOINL void CPE_WriteDefineExBlock(Client *client, BlockID id, BlockDef *block); 57 | NOINL void CPE_WriteBulkBlockUpdate(Client *client, BulkBlockUpdate *bbu); 58 | NOINL void CPE_WriteFastMapInit(Client *client, cs_uint32 size); 59 | NOINL void CPE_WriteAddTextColor(Client *client, Color4* color, cs_char code); 60 | NOINL void CPE_WriteSetHotBar(Client *client, cs_byte order, BlockID block); 61 | NOINL void CPE_WriteSetSpawnPoint(Client *client, Vec *pos, Ang *ang); 62 | NOINL void CPE_WriteVelocityControl(Client *client, Vec *velocity, cs_byte mode); 63 | NOINL void CPE_WriteDefineEffect(Client *client, cs_byte id, CPEParticle *e); 64 | NOINL void CPE_WriteSpawnEffect(Client *client, cs_byte id, Vec *pos, Vec *origin); 65 | NOINL void CPE_WriteWeatherType(Client *client, cs_int8 type); 66 | NOINL void CPE_WriteTexturePack(Client *client, cs_str url); 67 | NOINL void CPE_WriteMapProperty(Client *client, cs_byte property, cs_int32 value); 68 | NOINL void CPE_WriteSetEntityProperty(Client *client, Client *other, EEntProp type, cs_int32 value); 69 | NOINL void CPE_WriteTwoWayPing(Client *client, cs_byte direction, cs_int16 num); 70 | NOINL void CPE_WriteSetModel(Client *client, Client *other); 71 | NOINL void CPE_WriteSetMapAppearance(Client *client, cs_int32 ver, CPEAppearance *apps); 72 | NOINL void CPE_WriteBlockPerm(Client *client, BlockID id, cs_bool allowPlace, cs_bool allowDestroy); 73 | NOINL void CPE_WriteDefineModel(Client *client, cs_byte id, CPEModel *mdoel); 74 | NOINL void CPE_WriteDefineModelPart(Client *client, cs_int32 ver, cs_byte id, CPEModelPart *part); 75 | NOINL void CPE_WriteUndefineModel(Client *client, cs_byte id); 76 | NOINL void CPE_WritePluginMessage(Client *client, cs_byte channel, cs_str message); 77 | NOINL void CPE_WriteExtEntityTeleport(Client *client, cs_byte behavior, Vec *pos, Ang *ang); 78 | NOINL void CPE_WriteLightingMode(Client *client, cs_byte mode, cs_bool locked); 79 | #endif 80 | 81 | API cs_bool Packet_Register(EPacketID id, cs_uint16 size, packetHandler handler); 82 | API cs_bool Packet_SetCPEHandler(EPacketID id, cs_uint32 hash, cs_int32 ver, cs_uint16 size, packetHandler handler); 83 | 84 | API cs_byte Proto_ReadString(cs_char **data, cs_str *dstptr); 85 | API cs_byte Proto_ReadStringNoAlloc(cs_char **data, cs_char *dst); 86 | API void Proto_ReadSVec(cs_char **dataptr, SVec *vec); 87 | API void Proto_ReadAng(cs_char **dataptr, Ang *ang); 88 | API void Proto_ReadFlSVec(cs_char **dataptr, Vec *vec); 89 | API void Proto_ReadFlVec(cs_char **dataptr, Vec *vec); 90 | 91 | API void Proto_WriteString(cs_char **dataptr, cs_str string); 92 | API void Proto_WriteFlVec(cs_char **dataptr, const Vec *vec); 93 | API void Proto_WriteFlSVec(cs_char **dataptr, const Vec *vec); 94 | API void Proto_WriteSVec(cs_char **dataptr, const SVec *vec); 95 | API void Proto_WriteAng(cs_char **dataptr, const Ang *ang); 96 | API void Proto_WriteColor3(cs_char **dataptr, const Color3* color); 97 | API void Proto_WriteColor4(cs_char **dataptr, const Color4* color); 98 | API void Proto_WriteByteColor3(cs_char **dataptr, const Color3* color); 99 | API void Proto_WriteByteColor4(cs_char **dataptr, const Color4* color); 100 | API void Proto_WriteNFloat(cs_char **dataptr, cs_uint32 n, cs_float *arr); 101 | 102 | API void CPE_RegisterServerExtension(cs_str name, cs_int32 version); 103 | #endif // PROTOCOL_H 104 | -------------------------------------------------------------------------------- /src/str.c: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | #include "platform.h" 3 | #include "str.h" 4 | #include 5 | #include 6 | 7 | cs_bool String_CaselessCompare(cs_str str1, cs_str str2) { 8 | if(!str1 || !str2) return false; 9 | cs_byte c1, c2; 10 | 11 | while(true) { 12 | c1 = *str1++, c2 = *str2++; 13 | if(c1 == c2 && c2 == '\0') return true; 14 | if(c1 >= 'A' && c1 <= 'Z') c1 += 32; 15 | if(c2 >= 'A' && c2 <= 'Z') c2 += 32; 16 | if(c1 != c2) return false; 17 | } 18 | } 19 | 20 | cs_bool String_CaselessCompare2(cs_str str1, cs_str str2, cs_size len) { 21 | if(!str1 || !str2) return false; 22 | cs_byte c1, c2; 23 | 24 | while(len--) { 25 | c1 = *str1++, c2 = *str2++; 26 | if(c1 >= 'A' && c1 <= 'Z') c1 += 32; 27 | if(c2 >= 'A' && c2 <= 'Z') c2 += 32; 28 | if(c1 != c2) return false; 29 | } 30 | 31 | return true; 32 | } 33 | 34 | cs_bool String_Compare(cs_str str1, cs_str str2) { 35 | if(!str1 || !str2) return false; 36 | cs_byte c1, c2; 37 | 38 | while(true) { 39 | c1 = *str1++, c2 = *str2++; 40 | if(c1 != c2) return false; 41 | if(c1 == '\0' && c2 == '\0') return true; 42 | } 43 | } 44 | 45 | cs_int32 String_ToInt(cs_str str) { 46 | return atoi(str); 47 | } 48 | 49 | cs_long String_StrToLong(cs_str str, cs_char **strend, cs_int32 radix) { 50 | return strtol(str, strend, radix); 51 | } 52 | 53 | cs_float String_ToFloat(cs_str str) { 54 | return (cs_float)atof(str); 55 | } 56 | 57 | cs_size String_Length(cs_str str) { 58 | cs_str s; 59 | for (s = str; *s; ++s); 60 | return s - str; 61 | } 62 | 63 | cs_size String_Append(cs_char *dst, cs_size len, cs_str src) { 64 | cs_size curr_len = String_Length(dst); 65 | return String_Copy(dst + curr_len, len - curr_len, src); 66 | } 67 | 68 | cs_char *String_Grow(cs_char *src, cs_size add, cs_size *new) { 69 | cs_size newp = String_Length(src) + add + 1; 70 | if(new) *new = newp; 71 | return Memory_Realloc(src, newp); 72 | } 73 | 74 | cs_size String_Copy(cs_char *dst, cs_size len, cs_str src) { 75 | cs_size avail = len; 76 | 77 | while(avail > 1 && (*dst++ = *src++) != '\0') avail--; 78 | *dst = '\0'; 79 | 80 | return len - avail; 81 | } 82 | 83 | cs_uint32 String_FormatError(cs_uint32 code, cs_char *buf, cs_size buflen, va_list *args) { 84 | #if defined(CORE_USE_WINDOWS) 85 | cs_int32 len = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 86 | NULL, code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, (cs_uint32)buflen, args); 87 | if(len > 0) { 88 | while(*buf++ != '\0') { 89 | if(*buf == '\r' || *buf == '\n') { 90 | *buf = '\0'; 91 | break; 92 | } 93 | } 94 | } 95 | return len; 96 | #elif defined(CORE_USE_UNIX) 97 | (void)args; 98 | return String_Copy(buf, buflen, strerror(code)); 99 | #endif 100 | } 101 | 102 | cs_int32 String_FormatBufVararg(cs_char *buf, cs_size len, cs_str str, va_list *args) { 103 | return vsnprintf(buf, len, str, *args); 104 | } 105 | 106 | cs_int32 String_FormatBuf(cs_char *buf, cs_size len, cs_str str, ...) { 107 | cs_int32 wrlen; 108 | va_list args; 109 | va_start(args, str); 110 | wrlen = String_FormatBufVararg(buf, len, str, &args); 111 | va_end(args); 112 | return wrlen; 113 | } 114 | 115 | cs_char *String_LastChar(cs_str str, cs_char sym) { 116 | return strrchr(str, sym); 117 | } 118 | 119 | cs_char *String_FirstChar(cs_str str, cs_char sym) { 120 | return strchr(str, sym); 121 | } 122 | 123 | cs_char *String_FindSubstr(cs_str str, cs_str strsrch) { 124 | return strstr(str, strsrch); 125 | } 126 | 127 | cs_str String_TrimExtension(cs_str str) { 128 | cs_char *ext = String_LastChar(str, '.'); 129 | if(ext) *ext = '\0'; 130 | return str; 131 | } 132 | 133 | cs_str String_AllocCopy(cs_str str) { 134 | cs_size len = String_Length(str) + 1; 135 | cs_char *ptr = Memory_Alloc(1, len); 136 | String_Copy(ptr, len, str); 137 | return (cs_str)ptr; 138 | } 139 | 140 | cs_str String_FromArgument(cs_str args, cs_int32 index) { 141 | if(!args) return NULL; 142 | 143 | do { 144 | if(index > 0 && *args == ' ') { 145 | --index; ++args; 146 | } 147 | if(index == 0) return args; 148 | } while(*args++ != '\0'); 149 | 150 | return NULL; 151 | } 152 | 153 | cs_size String_GetArgument(cs_str args, cs_char *arg, cs_size len, cs_int32 index) { 154 | if(len == 0 || args == NULL) return 0; 155 | cs_size avail = len; 156 | 157 | while(*args != '\0') { 158 | if(index > 0) { 159 | if(*args++ == ' ') --index; 160 | if(*args == '\0') return 0; 161 | } else { 162 | do { 163 | *arg++ = *args++; 164 | } while(--avail > 1 && *args != '\0' && *args != ' '); 165 | *arg = '\0'; 166 | break; 167 | } 168 | } 169 | 170 | return len - avail; 171 | } 172 | 173 | cs_uint32 String_CountArguments(cs_str args) { 174 | if(!args || *args == '\0') return 0; 175 | cs_uint32 cnt = 1; 176 | 177 | while(*args++ != '\0') 178 | if(*args == ' ') cnt++; 179 | 180 | return cnt; 181 | } 182 | 183 | cs_bool String_IsSafe(cs_str str) { 184 | for(cs_size i = 0; str[i] != '\0'; i++) 185 | if((str[i] == '.' && str[i + 1] == '.') || str[i] == '/' || str[i] == '\\') return false; 186 | return true; 187 | } 188 | 189 | cs_size String_SizeOfB64(cs_size inlen) { 190 | if (inlen % 3 != 0) 191 | inlen += 3 - (inlen % 3); 192 | inlen /= 3; 193 | inlen *= 4; 194 | return inlen; 195 | } 196 | 197 | static const cs_char b64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 198 | 199 | cs_size String_ToB64(const cs_byte *src, cs_size len, cs_char *dst) { 200 | cs_size elen = String_SizeOfB64(len); 201 | dst[elen] = '\0'; 202 | 203 | for (cs_size i = 0, j = 0; i < len; i += 3, j += 4) { 204 | cs_int32 v = src[i]; 205 | v = i + 1 < len ? v << 8 | src[i + 1] : v << 8; 206 | v = i + 2 < len ? v << 8 | src[i + 2] : v << 8; 207 | 208 | dst[j] = b64chars[(v >> 18) & 0x3F]; 209 | dst[j + 1] = b64chars[(v >> 12) & 0x3F]; 210 | if (i + 1 < len) { 211 | dst[j + 2] = b64chars[(v >> 6) & 0x3F]; 212 | } else { 213 | dst[j + 2] = '='; 214 | } 215 | if (i + 2 < len) { 216 | dst[j + 3] = b64chars[v & 0x3F]; 217 | } else { 218 | dst[j + 3] = '='; 219 | } 220 | } 221 | 222 | return elen; 223 | } 224 | -------------------------------------------------------------------------------- /src/tests/strings.c: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | #include "str.h" 3 | #include "tests.h" 4 | 5 | cs_bool Tests_Strings(void) { 6 | Tests_NewTask("String copying"); 7 | char str1[8] = {0}, 8 | str2[4] = {'l', 'o', 'l'}; 9 | Tests_Assert(String_Copy(str1, 8, str2) == 3, "copy first string to second"); 10 | Tests_Assert(String_Copy(str1, 3, str2) == 2, "copy first string to smaller second array"); 11 | 12 | Tests_NewTask("Char finding"); 13 | Tests_Assert(String_Compare(String_FirstChar("_compare_teststring", 'e'), "e_teststring"), "detect first 'e' character"); 14 | Tests_Assert(String_Compare(String_LastChar("_compare_teststring", 'e'), "eststring"), "detect last 'e' character"); 15 | Tests_Assert(String_LastChar("_compare_teststring", 'x') == NULL, "detect invalid character"); 16 | Tests_Assert(String_FindSubstr("afafolafafak", "afafa") != NULL, "detect substring"); 17 | Tests_Assert(String_FindSubstr("afafolafafak", "afafA") == NULL, "detect invalid substring"); 18 | Tests_Assert(String_Compare(String_FindSubstr("afafolaFafak", "aFafa"), "aFafak"), "check detected substring"); 19 | 20 | Tests_NewTask("Caseless strings compare"); 21 | Tests_Assert(String_CaselessCompare("_compare_test_string_", "_compare_test_string_"), "caseless compare identical strings"); 22 | Tests_Assert(String_CaselessCompare("_coMpare_tesT_String_", "_ComparE_teSt_sTriNg_"), "caseless compare strings with different cases"); 23 | Tests_Assert(String_CaselessCompare("_XoMpRre_tesT_StNing_", "_CoFparE_teSc_sTriNg_") == false, "caseless compare different strings"); 24 | Tests_Assert(String_CaselessCompare2("_fixed_size_compareFJS*fsdu89", "_fixed_size_compareHJKfhsdHUFHUSDIFuhsdh8yfSD(", 19), "caseless compare fixed strings"); 25 | 26 | Tests_NewTask("Cased strings compare"); 27 | Tests_Assert(String_Compare("_compare_test_string__", "_compare_test_string__"), "compare identical strings"); 28 | Tests_Assert(String_Compare("_compare_teSt_string__", "_compAre_test_strinG__") == false, "compare strings with different cases"); 29 | Tests_Assert(String_Compare("_compare_test_strin __", "_compare_test_string__") == false, "compare different strings"); 30 | Tests_Assert(String_Compare("_compare_test_string__", "_compare_test_string_") == false, "compare different sized strings"); 31 | 32 | Tests_NewTask("Create allocated copy of string"); 33 | cs_str al = String_AllocCopy("_AlLoCatEdStrInGGGG"); 34 | Tests_Assert(String_Compare(al, "_AlLoCatEdStrInGGGG"), "compare allocated and static strings"); 35 | 36 | Tests_NewTask("ASCII to integer"); 37 | Tests_Assert(String_ToInt("500") == 500, "convert ascii decimal to integer"); 38 | Tests_Assert(String_ToInt("x500") == 0, "convert non-decimal to integer"); 39 | Tests_Assert(String_ToInt("test") == 0, "convert regular string to integer"); 40 | Tests_Assert(String_HexToInt("DEAD") == 0xDEAD, "convert hex number to integer"); 41 | Tests_Assert(String_HexToInt("HATE") == 0, "treat regular string as a hex number, then convert to to integer"); 42 | 43 | Tests_NewTask("ASCII to float"); 44 | Tests_Assert(String_ToFloat("0.5") == 0.5f, "convert ascii text to float #1"); 45 | Tests_Assert(String_ToFloat("13.37") == 13.37f, "convert ascii text to float #2"); 46 | Tests_Assert(String_ToFloat("0.50000001") == 0.5f, "convert ascii text to float #3"); 47 | 48 | Tests_NewTask("Check string length"); 49 | Tests_Assert(String_Length("_length_check_test") == 18, "check string length"); 50 | Tests_Assert(String_Length("") == 0, "check length of empty string"); 51 | 52 | Tests_NewTask("Append string to array"); 53 | cs_char *mem = Memory_Alloc(20, 1); 54 | Tests_Assert(String_Append(mem, 20, "_myteststring") == 13, "append first string to array"); 55 | Tests_Assert(String_Compare(mem, "_myteststring"), "check first appended string"); 56 | Tests_Assert(String_Append(mem, 20, "_add") == 4, "append second string to array"); 57 | Tests_Assert(String_Compare(mem, "_myteststring_add"), "check second appended string"); 58 | Tests_Assert(String_Append(mem, 20, "_ololo") == 2, "append third string to out of space array"); 59 | Tests_Assert(String_Compare(mem, "_myteststring_add_o"), "check third appended string"); 60 | 61 | Tests_NewTask("Grow string"); 62 | cs_size newsize = 0; 63 | Tests_Assert((mem = String_Grow(mem, 4, &newsize)) != NULL, "grow string"); 64 | Tests_Assert(newsize == 24, "check string size after growth"); 65 | Tests_Assert(String_Append(mem, newsize, "lolo") == 4, "append part of third string to growed string"); 66 | Memory_Free(mem); 67 | 68 | Tests_NewTask("Split string by spaces"); 69 | cs_str myteststring = "a1 b2 c3 dDd"; 70 | cs_char argtest[4]; 71 | Tests_Assert(String_Compare(String_FromArgument(myteststring, 0), "a1 b2 c3 dDd"), "check from first element"); 72 | Tests_Assert(String_Compare(String_FromArgument(myteststring, 1), "b2 c3 dDd"), "check from second element"); 73 | Tests_Assert(String_Compare(String_FromArgument(myteststring, 2), "c3 dDd"), "check from third element"); 74 | Tests_Assert(String_Compare(String_FromArgument(myteststring, 3), "dDd"), "check from fourth element"); 75 | Tests_Assert(String_GetArgument(myteststring, argtest, 4, 0) == 2, "get first element"); 76 | Tests_Assert(String_Compare(argtest, "a1"), "check first element"); 77 | Tests_Assert(String_GetArgument(myteststring, argtest, 4, 1) == 2, "get second element"); 78 | Tests_Assert(String_Compare(argtest, "b2"), "check second element"); 79 | Tests_Assert(String_GetArgument(myteststring, argtest, 4, 2) == 2, "get third element"); 80 | Tests_Assert(String_Compare(argtest, "c3"), "check third element"); 81 | Tests_Assert(String_GetArgument(myteststring, argtest, 4, 3) == 3, "get fourth element"); 82 | Tests_Assert(String_Compare(argtest, "dDd"), "check fourth element"); 83 | 84 | Tests_NewTask("Base64 encode"); 85 | cs_str mystring = "ololostring"; 86 | cs_size sz = String_Length(mystring), 87 | b64sz = String_SizeOfB64(sz); 88 | mem = Memory_Alloc(b64sz + 1, 1); 89 | Tests_Assert(String_ToB64((const cs_byte *)mystring, sz, mem) == b64sz, "encode string to base64"); 90 | Tests_Assert(String_Compare(mem, "b2xvbG9zdHJpbmc="), "check encoded string"); 91 | Memory_Free(mem); 92 | 93 | Tests_NewTask("Format strings"); 94 | cs_char buf[10]; 95 | Tests_Assert(String_FormatBuf(buf, 10, "%s_%x", "tEst", 1337) == 8, "format string"); 96 | Tests_Assert(String_Compare(buf, "tEst_539"), "check formatted string"); 97 | return true; 98 | } 99 | -------------------------------------------------------------------------------- /src/tests/world.c: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | #include "str.h" 3 | #include "tests.h" 4 | #include "block.h" 5 | #include "vector.h" 6 | #include "world.h" 7 | 8 | #define COMPARE_COLORS(c1, c2) ((c1).r == (c2).r || (c1).g == (c2).g || (c1).b == (c2).b) 9 | 10 | cs_bool Tests_World(void) { 11 | Tests_NewTask("Create world"); 12 | cs_str worldname = "__test"; 13 | World *world = World_Create(worldname); 14 | Tests_Assert(world != NULL, "create world structure"); 15 | SVec dims = {1024, 64, 1024}; 16 | World_SetDimensions(world, &dims); 17 | World_AllocBlockArray(world); 18 | cs_uint32 wsize = 0, 19 | ewsize = (cs_uint32)dims.x * (cs_uint32)dims.y * (cs_uint32)dims.z; 20 | Tests_Assert(World_GetBlockArray(world, &wsize) != NULL, "check block array"); 21 | Tests_Assert(wsize == ewsize, "check world data array size"); 22 | Tests_Assert(String_CaselessCompare(World_GetName(world), worldname), "check world name"); 23 | 24 | Tests_NewTask("Change world properties"); 25 | Tests_Assert(World_SetEnvProp(world, WORLD_PROP_SIDEBLOCK, BLOCK_DIRT), "set side block"); 26 | Tests_Assert(World_SetEnvProp(world, WORLD_PROP_EDGEBLOCK, BLOCK_GRASS), "set edge block"); 27 | Tests_Assert(World_SetEnvProp(world, WORLD_PROP_EDGELEVEL, 40), "set edge level"); 28 | Tests_Assert(World_SetEnvProp(world, WORLD_PROP_CLOUDSLEVEL, 228), "set clouds level"); 29 | Tests_Assert(World_SetEnvProp(world, WORLD_PROP_FOGDIST, 10), "set fog distance"); 30 | Tests_Assert(World_SetEnvProp(world, WORLD_PROP_SPDCLOUDS, 100), "set clouds speed"); 31 | Tests_Assert(World_SetEnvProp(world, WORLD_PROP_SPDWEATHER, 250), "set weather speed"); 32 | Tests_Assert(World_SetEnvProp(world, WORLD_PROP_FADEWEATHER, 250), "set weather fade"); 33 | Tests_Assert(World_SetEnvProp(world, WORLD_PROP_EXPFOG, 1), "set exponential fog"); 34 | Tests_Assert(World_SetEnvProp(world, WORLD_PROP_SIDEOFFSET, -6), "set map sides offset"); 35 | Tests_Assert(World_SetTexturePack(world, "http://test.texture/pack.zip"), "set texture pack"); 36 | Tests_Assert(World_SetWeather(world, WORLD_WEATHER_SNOW), "set weather"); 37 | Color3 skycol = {1, 2, 3}, cloudcol = {4, 5, 6}, 38 | fogcol = {7, 8, 9}, ambcol = {4, 5, 6}, diffcol = {4, 5, 6}; 39 | Tests_Assert(World_SetEnvColor(world, WORLD_COLOR_SKY, &skycol), "set env color"); 40 | Tests_Assert(World_SetEnvColor(world, WORLD_COLOR_CLOUD, &cloudcol), "set cloud color"); 41 | Tests_Assert(World_SetEnvColor(world, WORLD_COLOR_FOG, &fogcol), "set fog color"); 42 | Tests_Assert(World_SetEnvColor(world, WORLD_COLOR_AMBIENT, &ambcol), "set ambient color"); 43 | Tests_Assert(World_SetEnvColor(world, WORLD_COLOR_DIFFUSE, &diffcol), "set diffuse color"); 44 | 45 | Tests_NewTask("Place blocks in world"); 46 | SVec p1 = {.x = 1, .y = 3, .z = 3}, 47 | p2 = {.x = 2, .y = 2, .z = 8}, 48 | p3 = {.x = dims.x, .y = 0, .z = 0}; 49 | Tests_Assert(World_SetBlock(world, &p1, BLOCK_BEDROCK), "set first block"); 50 | Tests_Assert(World_SetBlock(world, &p2, BLOCK_LOG), "set second block"); 51 | Tests_Assert(World_SetBlockO(world, wsize - 1, BLOCK_WATER_STILL), "set third block by offset"); 52 | Tests_Assert(World_SetBlockO(world, wsize, BLOCK_WATER) == false, "set fourth block outside of world"); 53 | Tests_Assert(World_SetBlock(world, &p3, BLOCK_DIRT) == false, "set fifth block"); 54 | 55 | Tests_NewTask("Saving world"); 56 | Tests_Assert(World_Save(world), "unload world"); 57 | World_Lock(world, 0); 58 | World_Unlock(world); 59 | World_Free(world); 60 | 61 | Tests_NewTask("Load world"); 62 | world = World_Create(worldname); 63 | Tests_Assert(world != NULL, "create world structure"); 64 | Tests_Assert(World_Load(world), "load world"); 65 | World_Lock(world, 0); 66 | World_Unlock(world); 67 | 68 | Tests_NewTask("Checking world properties"); 69 | Tests_Assert(World_GetEnvProp(world, WORLD_PROP_SIDEBLOCK) == BLOCK_DIRT, "check side block"); 70 | Tests_Assert(World_GetEnvProp(world, WORLD_PROP_EDGEBLOCK) == BLOCK_GRASS, "check edge block"); 71 | Tests_Assert(World_GetEnvProp(world, WORLD_PROP_EDGELEVEL) == 40, "check edge level"); 72 | Tests_Assert(World_GetEnvProp(world, WORLD_PROP_CLOUDSLEVEL) == 228, "check clouds level"); 73 | Tests_Assert(World_GetEnvProp(world, WORLD_PROP_FOGDIST) == 10, "check fog distance"); 74 | Tests_Assert(World_GetEnvProp(world, WORLD_PROP_SPDCLOUDS) == 100, "check clouds speed"); 75 | Tests_Assert(World_GetEnvProp(world, WORLD_PROP_SPDWEATHER) == 250, "check weather speed"); 76 | Tests_Assert(World_GetEnvProp(world, WORLD_PROP_FADEWEATHER) == 250, "check weather fade"); 77 | Tests_Assert(World_GetEnvProp(world, WORLD_PROP_EXPFOG) == 1, "check exponential fog"); 78 | Tests_Assert(World_GetEnvProp(world, WORLD_PROP_SIDEOFFSET) == -6, "check map sides offset"); 79 | Tests_Assert(String_Compare(World_GetTexturePack(world), "http://test.texture/pack.zip"), "check texture pack"); 80 | Tests_Assert(World_GetWeather(world) == WORLD_WEATHER_SNOW, "check weather"); 81 | Color3 cskycol, cfogcol, ccloudcol, cambcol, cdiffcol; 82 | Tests_Assert(World_GetEnvColor(world, WORLD_COLOR_SKY, &cskycol), "reading sky color"); 83 | Tests_Assert(World_GetEnvColor(world, WORLD_COLOR_FOG, &cfogcol), "reading fog color"); 84 | Tests_Assert(World_GetEnvColor(world, WORLD_COLOR_CLOUD, &ccloudcol), "reading cloud color"); 85 | Tests_Assert(World_GetEnvColor(world, WORLD_COLOR_AMBIENT, &cambcol), "reading ambient color"); 86 | Tests_Assert(World_GetEnvColor(world, WORLD_COLOR_DIFFUSE, &cdiffcol), "reading diffuse color"); 87 | Tests_Assert(COMPARE_COLORS(cskycol, skycol), "check sky color"); 88 | Tests_Assert(COMPARE_COLORS(cfogcol, fogcol), "check fog color"); 89 | Tests_Assert(COMPARE_COLORS(ccloudcol, cloudcol), "check cloud color"); 90 | Tests_Assert(COMPARE_COLORS(cambcol, ambcol), "check ambient color"); 91 | Tests_Assert(COMPARE_COLORS(cdiffcol, diffcol), "check diffuse color"); 92 | 93 | Tests_NewTask("Check blocks placing"); 94 | Tests_Assert(World_GetBlock(world, &p1) == BLOCK_BEDROCK, "check first block"); 95 | Tests_Assert(World_GetBlock(world, &p2) == BLOCK_LOG, "check second block"); 96 | Tests_Assert(World_GetBlockO(world, wsize - 1) == BLOCK_WATER_STILL, "check third block by offset"); 97 | Tests_Assert(World_GetBlockO(world, wsize) == (BlockID)-1, "check block outside world"); 98 | World_FreeBlockArray(world); 99 | World_Free(world); 100 | 101 | return true; 102 | } 103 | -------------------------------------------------------------------------------- /src/compr.c: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | #include "compr.h" 3 | #include "platform.h" 4 | #include "strstor.h" 5 | #include "log.h" 6 | #include 7 | 8 | #if defined(CORE_USE_WINDOWS) 9 | # define CCONV __cdecl 10 | #elif defined(CORE_USE_UNIX) 11 | # define CCONV 12 | #endif 13 | 14 | static struct _ZLib { 15 | void *lib; 16 | 17 | unsigned long(CCONV *crc32)(unsigned long start, const unsigned char *data, unsigned int len); 18 | unsigned long(CCONV *zflags)(void); 19 | char *(CCONV *error)(int code); 20 | 21 | int(CCONV *definit)(z_streamp strm, int level, int meth, int bits, int memlvl, int strat, const char *ver, int size); 22 | int(CCONV *deflate)(z_streamp strm, int flush); 23 | int(CCONV *defend)(z_streamp strm); 24 | 25 | int(CCONV *infinit)(z_streamp strm, int bits, const char *ver, int size); 26 | int(CCONV *inflate)(z_streamp strm, int flush); 27 | int(CCONV *infend)(z_streamp strm); 28 | } zlib; 29 | 30 | static cs_str zsmylist[] = { 31 | "crc32", "zlibCompileFlags", "zError", 32 | "deflateInit2_", "deflate", "deflateEnd", 33 | "inflateInit2_", "inflate", "inflateEnd", 34 | NULL 35 | }; 36 | 37 | static cs_str zlibdll[] = { 38 | #if defined(CORE_USE_WINDOWS) 39 | # ifdef CORE_BUILD_DEBUG 40 | "zlibdwapi.dll", 41 | "zlibd1.dll", 42 | "zlibd.dll", 43 | # endif 44 | "zlibwapi.dll", 45 | "zlib1.dll", 46 | "zlib.dll", 47 | "libz.dll", 48 | #elif defined(CORE_USE_UNIX) 49 | "libz." DLIB_EXT ".1.2.12", 50 | "libz." DLIB_EXT ".1.2.11", 51 | "libz." DLIB_EXT ".1", 52 | "libz." DLIB_EXT, 53 | #else 54 | # error This file wants to be hacked 55 | #endif 56 | NULL 57 | }; 58 | 59 | INL static cs_bool InitBackend(void) { 60 | if(!zlib.lib && !DLib_LoadAll(zlibdll, zsmylist, (void **)&zlib)) 61 | return false; 62 | 63 | cs_ulong flags = zlib.zflags(); 64 | 65 | if(flags & BIT(17)) { 66 | Log_Error(Sstor_Get("Z_NOGZ")); 67 | return false; 68 | } 69 | 70 | if(flags & BIT(21)) { 71 | Log_Warn(Sstor_Get("Z_LVL1")); 72 | Log_Warn(Sstor_Get("Z_LVL2")); 73 | Log_Warn(Sstor_Get("Z_LVL3")); 74 | } 75 | 76 | return true; 77 | } 78 | 79 | INL static cs_int32 getWndBits(ComprType type) { 80 | switch(type) { 81 | case COMPR_TYPE_DEFLATE: 82 | case COMPR_TYPE_INFLATE: 83 | return -15; 84 | case COMPR_TYPE_UNGZIP: 85 | case COMPR_TYPE_GZIP: 86 | return 31; 87 | case COMPR_TYPE_NOTSET: 88 | default: 89 | return 0; 90 | } 91 | } 92 | 93 | cs_bool Compr_Init(Compr *ctx, ComprType type) { 94 | if(!zlib.lib && !InitBackend()) return false; 95 | 96 | if(!ctx->stream) ctx->stream = Memory_Alloc(1, sizeof(z_stream)); 97 | ctx->state = COMPR_STATE_IDLE; 98 | ctx->type = type; 99 | 100 | if(type == COMPR_TYPE_DEFLATE || type == COMPR_TYPE_GZIP) 101 | ctx->ret = zlib.definit( 102 | ctx->stream, Z_DEFAULT_COMPRESSION, 103 | Z_DEFLATED, getWndBits(type), 104 | MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY, 105 | ZLIB_VERSION, sizeof(z_stream) 106 | ); 107 | else if(type == COMPR_TYPE_INFLATE || type == COMPR_TYPE_UNGZIP) 108 | ctx->ret = zlib.infinit( 109 | ctx->stream, getWndBits(type), 110 | ZLIB_VERSION, sizeof(z_stream) 111 | ); 112 | 113 | return ctx->ret == Z_OK; 114 | } 115 | 116 | cs_bool Compr_IsInState(Compr *ctx, ComprState state) { 117 | return ctx->state == state; 118 | } 119 | 120 | cs_ulong Compr_CRC32(const cs_byte *data, cs_uint32 len) { 121 | if(!zlib.crc32) return 0x00000000; 122 | return zlib.crc32(0, data, len); 123 | } 124 | 125 | void Compr_SetInBuffer(Compr *ctx, void *data, cs_uint32 size) { 126 | if(!ctx->stream) return; 127 | z_streamp stream = (z_streamp)ctx->stream; 128 | ctx->state = COMPR_STATE_INPROCESS; 129 | stream->avail_in = size; 130 | stream->next_in = data; 131 | } 132 | 133 | void Compr_SetOutBuffer(Compr *ctx, void *data, cs_uint32 size) { 134 | if(!ctx->stream) return; 135 | z_streamp stream = (z_streamp)ctx->stream; 136 | stream->avail_out = size; 137 | stream->next_out = data; 138 | } 139 | 140 | INL static cs_bool DeflateStep(Compr *ctx) { 141 | if(!ctx->stream || !zlib.deflate) return false; 142 | z_streamp stream = (z_streamp)ctx->stream; 143 | cs_uint32 outbuf_size = stream->avail_out; 144 | 145 | ctx->ret = zlib.deflate(stream, ctx->state == COMPR_STATE_FINISHING ? Z_FINISH : Z_NO_FLUSH); 146 | 147 | if(ctx->state == COMPR_STATE_FINISHING && stream->avail_out == outbuf_size) 148 | ctx->state = COMPR_STATE_DONE; 149 | else if(ctx->state == COMPR_STATE_INPROCESS && stream->avail_out == outbuf_size) 150 | ctx->state = COMPR_STATE_FINISHING; 151 | 152 | ctx->written = outbuf_size - stream->avail_out; 153 | ctx->queued = stream->avail_in; 154 | 155 | return true; 156 | } 157 | 158 | INL static cs_bool InflateStep(Compr *ctx) { 159 | if(!ctx->stream || !zlib.inflate) return false; 160 | z_streamp stream = (z_streamp)ctx->stream; 161 | cs_uint32 avail = stream->avail_out; 162 | ctx->written = 0; 163 | ctx->ret = zlib.inflate(stream, Z_NO_FLUSH); 164 | switch(ctx->ret) { 165 | case Z_NEED_DICT: 166 | case Z_DATA_ERROR: 167 | case Z_MEM_ERROR: 168 | return false; 169 | } 170 | ctx->written = avail - stream->avail_out; 171 | ctx->queued = stream->avail_in; 172 | return true; 173 | } 174 | 175 | cs_bool Compr_Update(Compr *ctx) { 176 | if(ctx->state == COMPR_STATE_IDLE) 177 | ctx->state = COMPR_STATE_INPROCESS; 178 | else if(ctx->state == COMPR_STATE_DONE) 179 | return true; 180 | 181 | if(ctx->type == COMPR_TYPE_DEFLATE || ctx->type == COMPR_TYPE_GZIP) 182 | return DeflateStep(ctx); 183 | else if(ctx->type == COMPR_TYPE_INFLATE || ctx->type == COMPR_TYPE_UNGZIP) 184 | return InflateStep(ctx); 185 | 186 | return false; 187 | } 188 | 189 | cs_str Compr_GetLastError(Compr *ctx) { 190 | return Compr_GetError(ctx->ret); 191 | } 192 | 193 | cs_str Compr_GetError(cs_int32 code) { 194 | if(!zlib.error) return "zlib is not loaded"; 195 | return zlib.error(code); 196 | } 197 | 198 | cs_uint32 Compr_GetQueuedSize(Compr *ctx) { 199 | return ctx->queued; 200 | } 201 | 202 | cs_uint32 Compr_GetWrittenSize(Compr *ctx) { 203 | return ctx->written; 204 | } 205 | 206 | void Compr_Reset(Compr *ctx) { 207 | if(ctx->stream) { 208 | if(ctx->type == COMPR_TYPE_DEFLATE || ctx->type == COMPR_TYPE_GZIP) 209 | zlib.defend(ctx->stream); 210 | else if(ctx->type == COMPR_TYPE_INFLATE || ctx->type == COMPR_TYPE_UNGZIP) 211 | zlib.infend(ctx->stream); 212 | Memory_Zero(ctx->stream, sizeof(z_stream)); 213 | } 214 | ctx->type = COMPR_TYPE_NOTSET; 215 | ctx->state = COMPR_STATE_IDLE; 216 | } 217 | 218 | void Compr_Cleanup(Compr *ctx) { 219 | if(ctx->stream) { 220 | Memory_Free(ctx->stream); 221 | ctx->stream = NULL; 222 | } 223 | } 224 | 225 | void Compr_Uninit(void) { 226 | if(!zlib.lib) return; 227 | DLib_Unload(zlib.lib); 228 | Memory_Zero(&zlib, sizeof(zlib)); 229 | } 230 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |

12 | 13 | # cserver 14 | Another Minecraft Classic server in C. 15 | The server is still under development (see [Projects](https://github.com/igor725/cserver/projects?type=classic) tab)! 16 | 17 | The goal of this project is to create a stable, customizable and future-rich multiplatform Minecraft Classic server with a minimum dependencies. 18 | 19 | ## Features 20 | * Classic Protocol Extension 21 | * Multiplatform (Windows/Linux/macOS) 22 | * Plugins support 23 | * Web client support ([More info](https://www.classicube.net/api/docs/server#footer)) 24 | * Lua scripting (Implemented in the [Lua plugin](https://github.com/igor725/cs-lua)) 25 | * In browser control panel (Implemented in the [WebAdmin plugin](https://github.com/igor725/cs-web)) 26 | * Own world generator (Written by [scaled](https://github.com/scaledteam) for [LuaClassic](https://github.com/igor725/LuaClassic), later ported to cserver by me) 27 | * Heartbeat API (ClassiCube heartbeat implemented in the [Base plugin](https://github.com/igor725/cs-base)) 28 | * Easy configurable 29 | 30 | ## Download 31 | If you don't want to mess with compilers, you can always download the release build for your OS [here](https://github.com/igor725/cserver/releases). 32 | You can also get the latest unstable build [here](https://github.com/igor725/cserver/actions/workflows/build.yml). 33 | 34 | ## Dependencies 35 | 36 | ### On Linux/macOS 37 | 1. zlib 38 | 2. pthread 39 | 3. libcurl, libcrypto (will be loaded on demand) 40 | 4. libreadline (will be loaded if available) 41 | 42 | ### On Windows 43 | 1. zlib (will be automatically cloned and compiled during the building process) 44 | 2. Several std libs (such as Kernel32, DbgHelp, WS2_32) 45 | 3. WinInet, Advapi32 (will be loaded on demand) 46 | 47 | ### NOTES 48 | * Some libraries (such as libcurl, libcrypto) are multiplatform. You can use them both on Windows, Linux and macOS. 49 | * You can use zlib-ng in compatibility mode instead of zlib. 50 | 51 | ### Available HTTP backends 52 | - libcurl (`HTTP_USE_CURL_BACKEND`) 53 | - WinInet (`HTTP_USE_WININET_BACKEND`) 54 | 55 | ### Available crypto backends 56 | - libcrypto (`HASH_USE_CRYPTO_BACKEND`) 57 | - WinCrypt (`HASH_USE_WINCRYPT_BACKEND`) 58 | 59 | Let's say you want to compile the server for Windows with libcurl and libcrypto backends, then you should add these defines: 60 | `/DCORE_MANUAL_BACKENDS /DCORE_USE_WINDOWS_TYPES /DCORE_USE_WINDOWS_PATHS /DCORE_USE_WINDOWS_DEFINES /DHTTP_USE_CURL_BACKEND /DHASH_USE_CRYPTO_BACKEND` 61 | 62 | It can be done by creating a file called `vars.bat` in the root folder of the server with the following content: 63 | ```batch 64 | SET CFLAGS=!CFLAGS! /DCORE_MANUAL_BACKENDS ^ 65 | /DCORE_USE_WINDOWS ^ 66 | /DCORE_USE_WINDOWS_TYPES ^ 67 | /DCORE_USE_WINDOWS_PATHS ^ 68 | /DCORE_USE_WINDOWS_DEFINES ^ 69 | /DHTTP_USE_CURL_BACKEND ^ 70 | /DHASH_USE_CRYPTO_BACKEND 71 | ``` 72 | 73 | ## Building 74 | 75 | ### On Linux/macOS 76 | ``./build [args ...]`` 77 | 78 | Single command builder for Linux: `curl -sL https://igvx.ru/singlecommand | bash` (server + base + lua) 79 | 80 | NOTE: This script uses gcc, but you can change it to another compiler by setting CC environment variable (``CC=clang ./build [args ...]``). 81 | 82 | ### On Windows 83 | ``.\build.bat [args ...]`` 84 | 85 | NOTE: You must open a Visual Studio Command Prompt to run this script. 86 | 87 | ### Build script arguments 88 | 89 | | Argument | Description | 90 | | :---: | :---: | 91 | | cls | Clear console window before compilation | 92 | | upd | Pull latest server (or plugin) changes from a remote repository before building | 93 | | dbg | Build with debug symbols | 94 | | wall | Enable all possible warnings | 95 | | wx | Treat warnings as errors | 96 | | w0 | Disable all warnings | 97 | | od | Disable compiler optimizations | 98 | | san | Add LLVM sanitizers | 99 | | run | Start the server after compilation | 100 | | noprompt | Suppress zlib download prompt message (Windows only) | 101 | | pb | Build a plugin (See notes below) | 102 | 103 | #### Plugin arguments 104 | Notice that these arguments must be passed **after** the `pb` argument and plugin's name! 105 | 106 | | Argument | Description | 107 | | :---: | :---: | 108 | | install | Copy the plugin to the ``plugins`` directory after compilation | 109 | 110 | #### Notes 111 | * Each uncompiled plugin is a folder with a `cs-` prefix in its name. Inside this folder there should be a `src` folder with atleast one *.c file. 112 | * After compiling the plugin, another folder called `out` is created in its root. Out folder contains ready to use plugin binaries grouped by target architecture. 113 | * The next argument after `pb` must be the name of the plugin you want to build. The name **must not** include the `cs-` prefix. 114 | * Some plugins can define their own building arguments, these arguments can be found in the `vars.sh`/`vars.bat` file in the plugin's root folder. 115 | 116 | ### Example 117 | * ``./build`` - Build the server release binary 118 | * ``./build dbg wall upd`` - Pull latest changes from this repository, then build the server with all warnings and debug symbols 119 | * ``./build dbg wall pb base install`` - Build the base plugin with all warnings and debug symbols, then copy binary to the plugins directory 120 | * ``./build dbg wall upd pb base install`` - Pull latest changes from cs-base repository, then build the base plugin and copy binary to the plugins directory 121 | 122 | ## Notes 123 | * Use this software carefully! The server **may** have many security holes. 124 | * At this point, it is strongly recommended to recompile **all plugins** every time you update the server, otherwise your server may crash due to API incompatibility. 125 | * By default the server doesn't have any useful chat commands, build the [cs-base](https://github.com/igor725/cs-base) plugin for an expanded command set. 126 | * Here is the [example plugin](https://github.com/igor725/cs-test) for this server software. 127 | * Your directory should have the following structure in order to compile plugins: 128 | ``` 129 | [root_folder]/cserver - This repository 130 | [root_folder]/cs-lua - Lua scripting plugin 131 | [root_folder]/cs-base - Base server functionality 132 | [root_folder]/cs-survival - Survival plugin 133 | [root_folder]/cs-test - Test plugin 134 | ``` 135 | -------------------------------------------------------------------------------- /src/plugin.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file plugin.h 3 | * @author igor725 (gvaldovigor@gmail.com) 4 | * @brief Этот файл должен подключаться каждым плагином сервера. 5 | * Он реализует основные макросы и экспортирует функции плагина. 6 | * Примеры плагинов для сервера можно найти здесь: 7 | * https://github.com/igor725/cs-lua 8 | * https://github.com/igor725/cs-base 9 | * https://github.com/igor725/cs-survival 10 | * https://github.com/igor725/cs-worldedit 11 | * 12 | * @copyright Copyright (c) 2022 13 | * 14 | */ 15 | 16 | #ifndef PLUGIN_H 17 | #define PLUGIN_H 18 | #include "core.h" 19 | #include "platform.h" 20 | #include "types/plugin.h" 21 | 22 | #ifndef CORE_BUILD_PLUGIN 23 | # define Plugin_Lock(_p) Mutex_Lock((_p)->mutex) 24 | # define Plugin_Unlock(_p) Mutex_Unlock((_p)->mutex) 25 | 26 | void Plugin_LoadAll(void); 27 | void Plugin_UnloadAll(cs_bool force); 28 | 29 | /** 30 | * @brief Загружает указанный плагин. 31 | * 32 | * @param name название плагина 33 | * @param ignoredep игнорировать несовместимость версий PluginAPI 34 | * @return true - плагин загружен успешно, false - произошли 35 | * технические шоколадки. 36 | */ 37 | cs_bool Plugin_LoadDll(cs_str name, cs_bool ignoredep); 38 | 39 | /** 40 | * @brief Выгружает указанный плагин из памяти. 41 | * Параметр force может быть установлен в true только 42 | * и только тогда, когда сервер гарантированно не будет 43 | * больше взаимодействовать с эвентами, командами и прочим. 44 | * То есть перед завершением работы. Если проигнорировать 45 | * это правило, то в лучшем случае данные с которыми 46 | * оперирует плагин будут утеряны, в худшем - сервер крашнется. 47 | * 48 | * @param plugin название плагина 49 | * @param force будет ли проигнорировано значение, которое вернёт функция Plugin_Unload. 50 | * 51 | * @return true - плагин был выгружен успешно, false - что-то не так 52 | */ 53 | cs_bool Plugin_UnloadDll(Plugin *plugin, cs_bool force); 54 | 55 | /** 56 | * @brief Возвращает указатель на структуру 57 | * плагина, используется функцией Plugin_UnloadDll. 58 | * 59 | * Настоятельно рекомендуется не изменять вручную поля 60 | * структуры. Это может привести к неопределённому поведению. 61 | * 62 | * @param name название плагина 63 | * @return указатель на структуру плагина 64 | */ 65 | Plugin *Plugin_Get(cs_str name); 66 | 67 | // Массив всех загруженных плагинов 68 | extern Plugin *Plugins_List[MAX_PLUGINS]; 69 | #else 70 | /** 71 | * @brief Выполняется сервером в момент загрузки плагина. 72 | * Если функция вернёт false, плагин будет немедленно 73 | * выгружен из памяти. 74 | * 75 | * @return true - плагин успешно загрузился, false - произошёл какой-то прикол 76 | */ 77 | EXP cs_bool Plugin_Load(void); 78 | 79 | EXP cs_bool Plugin_LoadEx(cs_uint32 id); 80 | 81 | /** 82 | * @brief Выполняется сервером в момент выгрузки плагина. 83 | * Например, при вызове серверной команды /plugin unload 84 | * игроком, либо консолью. 85 | * 86 | * @param force true - возвращённое значение будет проигнорировано 87 | * и плагин выгрузится в любом случае, false - плагин не будет 88 | * выгружен, если эта функция вернёт false. 89 | * @return true - плагин может завершить свою работу сейчас, 90 | * false - плагин не может быть выгружен. 91 | */ 92 | EXP cs_bool Plugin_Unload(cs_bool force); 93 | 94 | /** 95 | * @brief Вызывается сервером, когда тот найдёт запрошенный 96 | * плагином интерфейс через Plugin_RequestInterface. 97 | * Если в данный момент никакой из плагинов не предоставляет 98 | * указанный интерфейс, сервер добавит запрос в холд-лист, 99 | * далее плагин должен терпеливо ждать вызова этой функции. 100 | * WARN: Каждый плагин лично ответственен за высвобождение 101 | * памяти, которую сервер выделил ему под интерфейс. То есть 102 | * каждый плагин, запросивший интерфейс должен сам высвободить 103 | * полученный параметр ptr. В том числе и при получении вызова 104 | * с размерностью 0 (отключением интерфейса). 105 | * 106 | * @param name название интерфейса 107 | * @param ptr указатель на структуру интерфейса (для каждого плагина она индивидуальна) 108 | * @param size размер структуры интерфейса 109 | */ 110 | EXP void Plugin_RecvInterface(cs_str name, void *ptr, cs_size size); 111 | 112 | // Небольшой макрос для облегчения жизни при объявлении интерфейсов плагина 113 | # define Plugin_DeclareInterfaces EXP PluginInterface Plugin_Interfaces[]; \ 114 | PluginInterface Plugin_Interfaces[] = 115 | // Макросы для создания массива интерфейсов 116 | # define PLUGIN_IFACE_END {NULL, NULL, 0} 117 | # define PLUGIN_IFACE_ADD(n, i) {n, (void *)&(i), sizeof(i)} 118 | 119 | /** 120 | * @brief Макрос, который должен выполнить каждый плагин. Он устанавливает версию API, 121 | * которую использует данный плагин в целях проверки совместимости с функциями, 122 | * предоставляемыми сервером. Также он устанавливает текущую версию плагина, которая 123 | * показывается при вызове команды /plugin list. 124 | * 125 | * P.S. На данном этапе развития сервера версия API не меняется, 126 | * она заморожена на значении 001, так как итоговый набор функций 127 | * ещё не сформирован окончательно. 128 | */ 129 | # define Plugin_SetVersion(ver) cs_int32 Plugin_ApiVer = PLUGIN_API_NUM, Plugin_Version = ver 130 | 131 | # define Plugin_SetURL(url) EXP cs_str Plugin_URL(void) { return url; } 132 | 133 | EXP cs_int32 Plugin_ApiVer, Plugin_Version; 134 | #endif 135 | 136 | API cs_bool Plugin_Enable(cs_str name, cs_bool load); 137 | API cs_bool Plugin_PerformUnload(cs_str name, cs_bool disable); 138 | API cs_uint32 Plugin_RequestInfo(PluginInfo *pi, cs_uint32 id); 139 | API void Plugin_DiscardInfo(PluginInfo *pi); 140 | 141 | /** 142 | * @brief Запрашивает у сервера указанный интерфейс. 143 | * 144 | * Если эта функция вернула true, это ещё не гарантирует то, 145 | * что плагин получит указанный интерфейс. Он придёт только тогда 146 | * когда в сервер загрузится плагин, реализующий его. До этого 147 | * момента запрос будет находиться в холд-листе. Если указанный 148 | * интерфейс будет найден в момент выполнения этой функции, то 149 | * ей же будет вызван коллбек Plugin_RecvInterface. 150 | * 151 | * Указатель на Plugin_RecvInterface используется в идентификационных 152 | * целях, чтобы сервер знал, какому плагину нужно отправить интерфейс. 153 | * 154 | * Названия интерфейсов чувствительны к регистру!! 155 | * 156 | * @param irecv указатель на функцию Plugin_RecvInterface 157 | * @param iname название интерфейса 158 | * @return true - запрос успешен, false - этот интерфейс уже был запрошен 159 | */ 160 | API cs_bool Plugin_RequestInterface(pluginReceiveIface irecv, cs_str iname); 161 | 162 | /** 163 | * @brief Отключает указанный интерфейс от плагина. 164 | * 165 | * @param irecv указатель на функцию Plugin_RecvInterface 166 | * @param iname название интерфейса 167 | * @return true - интерфейс отключен, false - такой интерфейс не был запрошел 168 | */ 169 | API cs_bool Plugin_DiscardInterface(pluginReceiveIface irecv, cs_str iname); 170 | #endif // PLUGIN_H 171 | -------------------------------------------------------------------------------- /src/log.c: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | #include "str.h" 3 | #include "platform.h" 4 | #include "log.h" 5 | #include "event.h" 6 | #include "consoleio.h" 7 | #include 8 | 9 | cs_byte Log_Flags = LOG_ALL; 10 | static Mutex *logMutex = NULL; 11 | 12 | #define MKCOL(c) "\033["c"m" 13 | #define MKTCOL(c, t) Log_Flags & LOG_COLORS ? (MKCOL(c) t MKCOL("0")) : t 14 | 15 | static cs_str MapColor(cs_char col) { 16 | if(col >= 'A' && col <= 'Z') col += 32; 17 | 18 | switch(col) { 19 | case '0': return MKCOL("30"); 20 | case '1': return MKCOL("34"); 21 | case '2': return MKCOL("32"); 22 | case '3': return MKCOL("36"); 23 | case '4': return MKCOL("31"); 24 | case '5': return MKCOL("35"); 25 | case '6': return MKCOL("2;33"); 26 | case '7': return MKCOL("1;30"); 27 | case '8': return MKCOL("2;37"); 28 | case '9': return MKCOL("1;34"); 29 | case 'a': return MKCOL("1;32"); 30 | case 'b': return MKCOL("1;34"); 31 | case 'c': return MKCOL("1;31"); 32 | case 'd': return MKCOL("1;35"); 33 | case 'e': return MKCOL("33"); 34 | case 'f': return MKCOL("0"); 35 | } 36 | 37 | return NULL; 38 | } 39 | 40 | INL static cs_str GetName(cs_byte flag) { 41 | switch(flag) { 42 | case LOG_ERROR: 43 | return MKTCOL("1;31", "ERROR"); 44 | case LOG_INFO: 45 | return MKTCOL("1;32", "INFO "); 46 | case LOG_CHAT: 47 | return MKTCOL("1;33", "CHAT "); 48 | case LOG_WARN: 49 | return MKTCOL("35", "WARN "); 50 | case LOG_DEBUG: 51 | return MKTCOL("1;34", "DEBUG"); 52 | } 53 | return NULL; 54 | } 55 | 56 | cs_bool Log_Init(void) { 57 | return (logMutex = Mutex_Create()) != NULL; 58 | } 59 | 60 | void Log_Uninit(void) { 61 | if(logMutex) Mutex_Free(logMutex); 62 | } 63 | 64 | void Log_SetLevelStr(cs_str str) { 65 | cs_byte level = LOG_ERROR; 66 | 67 | do { 68 | switch (*str) { 69 | case 'c': 70 | level |= LOG_COLORS; 71 | break; 72 | case 'r': 73 | level |= LOG_REPEAT; 74 | break; 75 | case 'I': 76 | level |= LOG_INFO; 77 | break; 78 | case 'C': 79 | level |= LOG_CHAT; 80 | break; 81 | case 'W': 82 | level |= LOG_WARN; 83 | break; 84 | case 'D': 85 | level |= LOG_DEBUG; 86 | break; 87 | case 'Q': 88 | level = LOG_QUIET; 89 | break; 90 | } 91 | } while(*str++ != '\0'); 92 | 93 | Log_Flags = level; 94 | } 95 | 96 | static LogBuffer buffer = { 97 | .offset = 0 98 | }; 99 | 100 | static struct LogPrevLine { 101 | cs_char buffer[LOG_BUFSIZE]; 102 | cs_size count; 103 | cs_byte flag; 104 | } prev; 105 | 106 | // Сдвигает все символы в лог буфере на указанное количество байт влево 107 | INL static void BufShiftLeft(cs_size from, cs_size shift) { 108 | for(cs_size i = from + shift; i < buffer.offset; i++) { 109 | buffer.data[i - shift] = buffer.data[i]; 110 | if(buffer.data[i] == '\0') break; 111 | } 112 | buffer.offset -= shift; 113 | } 114 | 115 | // Сдвигает все символы в лог буфере на указанное количество байт вправо 116 | INL static cs_bool BufShiftRight(cs_size from, cs_size shift) { 117 | if(shift == 0) return false; 118 | for(cs_size i = buffer.offset; i >= from; i--) { 119 | if(LOG_BUFSIZE > i + shift) 120 | buffer.data[i + shift] = buffer.data[i]; 121 | buffer.data[i] = '\0'; 122 | } 123 | buffer.offset += shift; 124 | return true; 125 | } 126 | 127 | void Log_Print(cs_byte flag, cs_str str, va_list *args) { 128 | if(Log_Flags & flag) { 129 | Mutex_Lock(logMutex); 130 | 131 | // Не даём серверу принтить одинаковые строки по миллон раз 132 | if(Log_Flags & LOG_REPEAT) { 133 | if(String_CaselessCompare(str, prev.buffer) && prev.flag == flag) { 134 | ConsoleIO_PrePrint(); 135 | File_WriteFormat(stderr, "\033[u (x%d)\r\n", prev.count++); 136 | File_Flush(stderr); 137 | ConsoleIO_AfterPrint(); 138 | goto logend; 139 | } else { 140 | String_Copy(prev.buffer, LOG_BUFSIZE, str); 141 | prev.flag = flag; 142 | prev.count = 1; 143 | } 144 | } 145 | 146 | cs_int32 ret; 147 | if((ret = Time_Format(buffer.data, LOG_BUFSIZE)) > 0) 148 | buffer.offset = ret; 149 | 150 | if((ret = String_FormatBuf(buffer.data + buffer.offset, 151 | LOG_BUFSIZE - buffer.offset, " [%s] ", GetName(flag) 152 | )) > 0) 153 | buffer.offset += ret; 154 | 155 | if(args) { 156 | if((ret = String_FormatBufVararg( 157 | buffer.data + buffer.offset, 158 | LOG_BUFSIZE - buffer.offset, str, args 159 | )) > 0) 160 | buffer.offset += ret; 161 | else goto logend; 162 | } else { 163 | if((ret = (cs_int32)String_Append( 164 | buffer.data + buffer.offset, 165 | LOG_BUFSIZE - buffer.offset, str 166 | )) > 0) 167 | buffer.offset += ret; 168 | else goto logend; 169 | } 170 | 171 | cs_char lastcolor = '\0'; 172 | for(cs_size i = 0; i < buffer.offset; i++) { 173 | if((buffer.data[i] == '&' || buffer.data[i] == '%') && ISHEX(buffer.data[i + 1])) { 174 | cs_char currcol = buffer.data[i + 1]; 175 | if(Log_Flags & LOG_COLORS && lastcolor != currcol) { 176 | cs_str color = MapColor(currcol); 177 | if(color) { 178 | cs_size clen = String_Length(color); 179 | if(clen == 0) continue; 180 | if(clen > 2) BufShiftRight(i + 2, clen - 2); 181 | Memory_Copy(buffer.data + i, color, clen); 182 | lastcolor = currcol; 183 | // Отнимаем 1 потому что цикл и так заинкрементит значение i 184 | i += clen - 1; 185 | continue; 186 | } 187 | } 188 | BufShiftLeft(i, 2); 189 | } 190 | } 191 | 192 | // Убеждаемся, что завершающие символы у нас поместятся в буфер 193 | buffer.offset = min(buffer.offset, LOG_BUFSIZE - 8); 194 | if(Log_Flags & LOG_COLORS) 195 | buffer.offset += String_Copy( 196 | buffer.data + buffer.offset, 197 | LOG_BUFSIZE - buffer.offset, 198 | MapColor('f') 199 | ); 200 | buffer.data[buffer.offset] = '\0'; 201 | buffer.flag = flag; 202 | 203 | if(Event_Call(EVT_ONLOG, &buffer)) { 204 | ConsoleIO_PrePrint(); 205 | File_Write(buffer.data, buffer.offset, 1, stderr); 206 | if(Log_Flags & LOG_REPEAT) 207 | File_Write("\033[s\r\n", 5, 1, stderr); 208 | else 209 | File_Write("\r\n", 2, 1, stderr); 210 | File_Flush(stderr); 211 | ConsoleIO_AfterPrint(); 212 | } 213 | 214 | logend: 215 | Mutex_Unlock(logMutex); 216 | } 217 | } 218 | 219 | void Log_Gen(cs_byte flag, cs_str str, ...) { 220 | va_list args; 221 | va_start(args, str); 222 | Log_Print(flag, str, &args); 223 | va_end(args); 224 | } 225 | 226 | void Log_Error(cs_str str, ...) { 227 | va_list args; 228 | va_start(args, str); 229 | Log_Print(LOG_ERROR, str, &args); 230 | va_end(args); 231 | } 232 | 233 | void Log_Info(cs_str str, ...) { 234 | va_list args; 235 | va_start(args, str); 236 | Log_Print(LOG_INFO, str, &args); 237 | va_end(args); 238 | } 239 | 240 | void Log_Chat(cs_str str, ...) { 241 | Log_Print(LOG_CHAT, str, (va_list *)NULL); 242 | } 243 | 244 | void Log_Warn(cs_str str, ...) { 245 | va_list args; 246 | va_start(args, str); 247 | Log_Print(LOG_WARN, str, &args); 248 | va_end(args); 249 | } 250 | 251 | void Log_Debug(cs_str str, ...) { 252 | va_list args; 253 | va_start(args, str); 254 | Log_Print(LOG_DEBUG, str, &args); 255 | va_end(args); 256 | } 257 | -------------------------------------------------------------------------------- /src/cpe.c: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | #include "client.h" 3 | #include "protocol.h" 4 | #include "types/world.h" 5 | #include "block.h" 6 | #include "str.h" 7 | #include "cpe.h" 8 | 9 | /** 10 | * CustomModel 11 | */ 12 | 13 | static cs_str originalModelNames[16] = { 14 | "humanoid", "chicken", "creeper", 15 | "pig", "sheep", "skeleton", "sheep", 16 | "sheep_nofur", "skeleton", "spider", 17 | "zombie", "head", "sit", "chibi", 18 | NULL 19 | }; 20 | 21 | static CPEModel *customModels[CPE_MAX_MODELS] = {NULL}; 22 | 23 | cs_bool CPE_IsModelDefined(cs_byte id) { 24 | if(id >= CPE_MAX_MODELS) return false; 25 | return customModels[id] != NULL || id < 15; 26 | } 27 | 28 | cs_bool CPE_IsModelDefinedPtr(CPEModel *model) { 29 | for(cs_int16 i = 0; i < CPE_MAX_MODELS; i++) { 30 | if(customModels[i] == model) 31 | return true; 32 | } 33 | return false; 34 | } 35 | 36 | cs_str CPE_GetDefaultModelName(void) { 37 | return customModels[0] ? customModels[0]->name : originalModelNames[0]; 38 | } 39 | 40 | CPEModel *CPE_GetModel(cs_byte id) { 41 | if(id >= CPE_MAX_MODELS) return NULL; 42 | return customModels[id]; 43 | } 44 | 45 | void CPE_SendModel(Client *client, cs_int32 extVer, cs_byte id) { 46 | if(id >= CPE_MAX_MODELS) return; 47 | CPEModel *model = customModels[id]; 48 | if(!extVer || !model) return; 49 | CPE_WriteDefineModel(client, id, model); 50 | CPEModelPart *part = model->part; 51 | while(part) { 52 | CPE_WriteDefineModelPart(client, extVer, id, part); 53 | part = part->next; 54 | } 55 | } 56 | 57 | cs_bool CPE_DefineModel(cs_byte id, CPEModel *model) { 58 | if(id >= CPE_MAX_MODELS) return false; 59 | if(!model->part || !model->partsCount) return false; 60 | if(CPE_IsModelDefinedPtr(model)) return false; 61 | customModels[id] = model; 62 | for(ClientID i = 0; i < MAX_CLIENTS; i++) { 63 | Client *client = Clients_List[i]; 64 | if(!client) continue; 65 | cs_int32 extVer = Client_GetExtVer(client, EXT_CUSTOMMODELS); 66 | CPE_SendModel(client, extVer, id); 67 | } 68 | return true; 69 | } 70 | 71 | cs_bool CPE_UndefineModel(cs_byte id) { 72 | if(id >= CPE_MAX_MODELS) return false; 73 | if(!customModels[id]) return false; 74 | customModels[id] = NULL; 75 | for(ClientID i = 0; i < MAX_CLIENTS; i++) { 76 | Client *client = Clients_List[i]; 77 | if(!client) continue; 78 | if(Client_GetExtVer(client, EXT_CUSTOMMODELS)) 79 | Client_UndefineModel(client, id); 80 | } 81 | return true; 82 | } 83 | 84 | cs_bool CPE_UndefineModelPtr(CPEModel *mdl) { 85 | for(cs_int16 i = 0; i < CPE_MAX_MODELS && mdl; i++) { 86 | if(customModels[i] == mdl) 87 | return CPE_UndefineModel((cs_byte)i); 88 | } 89 | return false; 90 | } 91 | 92 | cs_bool CPE_CheckModel(Client *client, cs_int16 model) { 93 | World *world = Client_GetWorld(client); 94 | if(world && model < 256) 95 | return Block_IsValid(world, (BlockID)model); 96 | return CPE_IsModelDefined(model % 256); 97 | } 98 | 99 | cs_int16 CPE_GetModelNum(cs_str model) { 100 | cs_int16 modelnum = -1; 101 | 102 | for(cs_int16 i = 0; i < CPE_MAX_MODELS; i++) { 103 | CPEModel *pmdl = customModels[i]; 104 | if(pmdl && String_CaselessCompare(pmdl->name, model)) { 105 | modelnum = i + 256; 106 | break; 107 | } 108 | } 109 | 110 | if(modelnum == -1) { 111 | for(cs_int16 i = 0; originalModelNames[i]; i++) { 112 | cs_str cmdl = originalModelNames[i]; 113 | if(!customModels[i] && String_CaselessCompare(model, cmdl)) { 114 | modelnum = i + 256; 115 | break; 116 | } 117 | } 118 | } 119 | 120 | if(modelnum == -1) { 121 | if(ISNUM(*model)) { 122 | cs_int32 tmp = String_ToInt(model); 123 | if(tmp >= 0 && tmp < 256) 124 | modelnum = (cs_int16)tmp; 125 | } else 126 | modelnum = 256; 127 | } 128 | 129 | return modelnum; 130 | } 131 | 132 | cs_uint32 CPE_GetModelStr(cs_int16 num, cs_char *buffer, cs_uint32 buflen) { 133 | if(num > 255) { // За пределами 256 первых id находятся неблоковые модели 134 | cs_byte modelid = num % CPE_MAX_MODELS; 135 | cs_str mdl = NULL; 136 | if(customModels[modelid]) 137 | mdl = customModels[modelid]->name; 138 | else if(modelid < 15) 139 | mdl = originalModelNames[modelid]; 140 | 141 | return mdl ? (cs_uint32)String_Copy(buffer, buflen, mdl) : 0; 142 | } 143 | 144 | cs_int32 ret = String_FormatBuf(buffer, buflen, "%d", num); 145 | return max(0, ret); 146 | } 147 | 148 | /** 149 | * CustomParticles 150 | */ 151 | 152 | static CPEParticle *customParticles[CPE_MAX_PARTICLES] = {NULL}; 153 | 154 | cs_bool CPE_IsParticleDefined(cs_byte id) { 155 | if(id >= CPE_MAX_PARTICLES) return false; 156 | return customParticles[id] != NULL; 157 | } 158 | 159 | cs_bool CPE_IsParticleDefinedPtr(CPEParticle *part) { 160 | for(cs_int16 i = 0; i < CPE_MAX_PARTICLES && part; i++) 161 | if(part == customParticles[i]) 162 | return true; 163 | return false; 164 | } 165 | 166 | CPEParticle *CPE_GetParticle(cs_byte id) { 167 | if(id >= CPE_MAX_PARTICLES) return NULL; 168 | return customParticles[id]; 169 | } 170 | 171 | void CPE_SendParticle(Client *client, cs_byte id) { 172 | if(id >= CPE_MAX_PARTICLES) return; 173 | CPEParticle *part = customParticles[id]; 174 | if(part) CPE_WriteDefineEffect(client, id, part); 175 | } 176 | 177 | cs_bool CPE_DefineParticle(cs_byte id, CPEParticle *part) { 178 | if(id >= CPE_MAX_PARTICLES) return false; 179 | if(customParticles[id]) return false; 180 | if(CPE_IsParticleDefinedPtr(part)) return false; 181 | customParticles[id] = part; 182 | for(ClientID i = 0; i < MAX_CLIENTS; i++) { 183 | Client *client = Clients_List[i]; 184 | if(!client) continue; 185 | if(Client_GetExtVer(client, EXT_CUSTOMPARTS)) 186 | CPE_SendParticle(client, id); 187 | } 188 | return true; 189 | } 190 | 191 | cs_bool CPE_UndefineParticle(cs_byte id) { 192 | if(id >= CPE_MAX_PARTICLES) return false; 193 | if(!customParticles[id]) return false; 194 | customParticles[id] = NULL; 195 | return true; 196 | } 197 | 198 | cs_bool CPE_UndefineParticlePtr(CPEParticle *ptr) { 199 | for(cs_int16 i = 0; i < CPE_MAX_PARTICLES && ptr; i++) { 200 | if(customParticles[i] == ptr) { 201 | customParticles[i] = NULL; 202 | return true; 203 | } 204 | } 205 | return false; 206 | } 207 | 208 | INL static void CubeNormalize(SVec *s, SVec *e) { 209 | cs_int16 tmp, *a = (cs_int16 *)s, *b = (cs_int16 *)e; 210 | for(int i = 0; i < 3; i++) { 211 | if(b[i] < a[i]) { 212 | tmp = b[i]; 213 | b[i] = a[i]; 214 | a[i] = tmp; 215 | } 216 | b[i]++; 217 | } 218 | } 219 | 220 | /** 221 | * SelectionCuboid 222 | */ 223 | 224 | void Cuboid_SetPositions(CPECuboid *cub, SVec start, SVec end) { 225 | cub->pos[0] = start, cub->pos[1] = end; 226 | CubeNormalize(&cub->pos[0], &cub->pos[1]); 227 | } 228 | 229 | void Cuboid_SetColor(CPECuboid *cub, Color4 color) { 230 | cub->color = color; 231 | } 232 | 233 | cs_byte Cuboid_GetID(CPECuboid *cub) { 234 | return cub->id; 235 | } 236 | 237 | cs_uint32 Cuboid_GetSize(CPECuboid *cub) { 238 | return (cub->pos[1].x - cub->pos[0].x) * 239 | (cub->pos[1].y - cub->pos[0].y) * 240 | (cub->pos[1].z - cub->pos[0].z); 241 | } 242 | 243 | void Cuboid_GetPositions(CPECuboid *cub, SVec *start, SVec *end) { 244 | if(start) *start = cub->pos[0]; 245 | if(end) *end = cub->pos[1]; 246 | } 247 | --------------------------------------------------------------------------------