| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| #if defined(CONF_SQL) | ||
|
|
||
| #include <base/system.h> | ||
|
|
||
| #include "sql_connector.h" | ||
|
|
||
| CSqlServer** CSqlConnector::ms_ppSqlReadServers = 0; | ||
| CSqlServer** CSqlConnector::ms_ppSqlWriteServers = 0; | ||
|
|
||
| int CSqlConnector::ms_ReachableReadServer = 0; | ||
| int CSqlConnector::ms_ReachableWriteServer = 0; | ||
|
|
||
| CSqlConnector::CSqlConnector() : | ||
| m_pSqlServer(0), | ||
| m_NumReadRetries(0), | ||
| m_NumWriteRetries(0) | ||
| {} | ||
|
|
||
| bool CSqlConnector::ConnectSqlServer(bool ReadOnly) | ||
| { | ||
| ReadOnly ? ++m_NumReadRetries : ++m_NumWriteRetries; | ||
| int& ReachableServer = ReadOnly ? ms_ReachableReadServer : ms_ReachableWriteServer; | ||
| int NumServers = ReadOnly ? CSqlServer::ms_NumReadServer : CSqlServer::ms_NumWriteServer; | ||
|
|
||
| for (int i = ReachableServer, ID = ReachableServer; i < ReachableServer + NumServers && SqlServer(i % NumServers, ReadOnly); i++, ID = i % NumServers) | ||
| { | ||
| if (SqlServer(ID, ReadOnly) && SqlServer(ID, ReadOnly)->Connect()) | ||
| { | ||
| m_pSqlServer = SqlServer(ID, ReadOnly); | ||
| ReachableServer = ID; | ||
| return true; | ||
| } | ||
| if (SqlServer(ID, ReadOnly)) | ||
| dbg_msg("sql", "Warning: Unable to connect to Sql%sServer %d ('%s'), trying next...", ReadOnly ? "Read" : "Write", ID, SqlServer(ID, ReadOnly)->GetIP()); | ||
| } | ||
| dbg_msg("sql", "FATAL ERROR: No Sql%sServers available", ReadOnly ? "Read" : "Write"); | ||
| m_pSqlServer = 0; | ||
| return false; | ||
| } | ||
|
|
||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| #ifndef ENGINE_SERVER_SQL_CONNECTOR_H | ||
| #define ENGINE_SERVER_SQL_CONNECTOR_H | ||
|
|
||
| #include "sql_server.h" | ||
|
|
||
| enum | ||
| { | ||
| MAX_SQLSERVERS=15 | ||
| }; | ||
|
|
||
| // implementation to provide sqlservers | ||
| class CSqlConnector | ||
| { | ||
| public: | ||
| CSqlConnector(); | ||
|
|
||
| CSqlServer* SqlServer(int i, bool ReadOnly = true) { return ReadOnly ? ms_ppSqlReadServers[i] : ms_ppSqlWriteServers[i]; } | ||
|
|
||
| // always returns the last connected sql-server | ||
| CSqlServer* SqlServer() { return m_pSqlServer; } | ||
|
|
||
| static void SetReadServers(CSqlServer** ppReadServers) { ms_ppSqlReadServers = ppReadServers; } | ||
| static void SetWriteServers(CSqlServer** ppWriteServers) { ms_ppSqlWriteServers = ppWriteServers; } | ||
|
|
||
| static void ResetReachable() { ms_ReachableReadServer = 0; ms_ReachableWriteServer = 0; } | ||
|
|
||
| bool ConnectSqlServer(bool ReadOnly = true); | ||
|
|
||
| bool MaxTriesReached(bool ReadOnly = true) { return ReadOnly ? m_NumReadRetries >= CSqlServer::ms_NumReadServer : m_NumWriteRetries >= CSqlServer::ms_NumWriteServer; } | ||
|
|
||
| private: | ||
|
|
||
| CSqlServer *m_pSqlServer; | ||
| static CSqlServer **ms_ppSqlReadServers; | ||
| static CSqlServer **ms_ppSqlWriteServers; | ||
|
|
||
| static int ms_NumReadServer; | ||
| static int ms_NumWriteServer; | ||
|
|
||
| static int ms_ReachableReadServer; | ||
| static int ms_ReachableWriteServer; | ||
|
|
||
| int m_NumReadRetries; | ||
| int m_NumWriteRetries; | ||
| }; | ||
|
|
||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,213 @@ | ||
| #if defined(CONF_SQL) | ||
|
|
||
| #include <base/system.h> | ||
| #include <engine/shared/protocol.h> | ||
| #include <engine/shared/config.h> | ||
|
|
||
| #include "sql_server.h" | ||
|
|
||
|
|
||
| int CSqlServer::ms_NumReadServer = 0; | ||
| int CSqlServer::ms_NumWriteServer = 0; | ||
|
|
||
| CSqlServer::CSqlServer(const char* pDatabase, const char* pPrefix, const char* pUser, const char* pPass, const char* pIp, int Port, bool ReadOnly, bool SetUpDb) : | ||
| m_Port(Port), | ||
| m_SetUpDB(SetUpDb) | ||
| { | ||
| str_copy(m_aDatabase, pDatabase, sizeof(m_aDatabase)); | ||
| str_copy(m_aPrefix, pPrefix, sizeof(m_aPrefix)); | ||
| str_copy(m_aUser, pUser, sizeof(m_aUser)); | ||
| str_copy(m_aPass, pPass, sizeof(m_aPass)); | ||
| str_copy(m_aIp, pIp, sizeof(m_aIp)); | ||
|
|
||
| m_pDriver = 0; | ||
| m_pConnection = 0; | ||
| m_pResults = 0; | ||
| m_pStatement = 0; | ||
|
|
||
| ReadOnly ? ms_NumReadServer++ : ms_NumWriteServer++; | ||
|
|
||
| m_SqlLock = lock_create(); | ||
| } | ||
|
|
||
| CSqlServer::~CSqlServer() | ||
| { | ||
| Lock(); | ||
| try | ||
| { | ||
| if (m_pResults) | ||
| delete m_pResults; | ||
| if (m_pConnection) | ||
| delete m_pConnection; | ||
| dbg_msg("sql", "SQL connection disconnected"); | ||
| } | ||
| catch (sql::SQLException &e) | ||
| { | ||
| dbg_msg("sql", "ERROR: No SQL connection"); | ||
| } | ||
| UnLock(); | ||
| lock_destroy(m_SqlLock); | ||
| } | ||
|
|
||
| bool CSqlServer::Connect() | ||
| { | ||
| Lock(); | ||
|
|
||
| if (m_pDriver != NULL && m_pConnection != NULL) | ||
| { | ||
| try | ||
| { | ||
| // Connect to specific database | ||
| m_pConnection->setSchema(m_aDatabase); | ||
| } | ||
| catch (sql::SQLException &e) | ||
| { | ||
| dbg_msg("sql", "MySQL Error: %s", e.what()); | ||
|
|
||
| dbg_msg("sql", "ERROR: SQL connection failed"); | ||
| UnLock(); | ||
| return false; | ||
| } | ||
| return true; | ||
| } | ||
|
|
||
| try | ||
| { | ||
| m_pDriver = 0; | ||
| m_pConnection = 0; | ||
| m_pStatement = 0; | ||
|
|
||
| sql::ConnectOptionsMap connection_properties; | ||
| connection_properties["hostName"] = sql::SQLString(m_aIp); | ||
| connection_properties["port"] = m_Port; | ||
| connection_properties["userName"] = sql::SQLString(m_aUser); | ||
| connection_properties["password"] = sql::SQLString(m_aPass); | ||
| connection_properties["OPT_CONNECT_TIMEOUT"] = 10; | ||
| connection_properties["OPT_READ_TIMEOUT"] = 10; | ||
| connection_properties["OPT_WRITE_TIMEOUT"] = 20; | ||
| connection_properties["OPT_RECONNECT"] = true; | ||
|
|
||
| // Create connection | ||
| m_pDriver = get_driver_instance(); | ||
| m_pConnection = m_pDriver->connect(connection_properties); | ||
|
|
||
| // Create Statement | ||
| m_pStatement = m_pConnection->createStatement(); | ||
|
|
||
| if (m_SetUpDB) | ||
| { | ||
| char aBuf[128]; | ||
| // create database | ||
| str_format(aBuf, sizeof(aBuf), "CREATE DATABASE IF NOT EXISTS %s", m_aDatabase); | ||
| m_pStatement->execute(aBuf); | ||
| } | ||
|
|
||
| // Connect to specific database | ||
| m_pConnection->setSchema(m_aDatabase); | ||
| dbg_msg("sql", "sql connection established"); | ||
| return true; | ||
| } | ||
| catch (sql::SQLException &e) | ||
| { | ||
| dbg_msg("sql", "MySQL Error: %s", e.what()); | ||
| dbg_msg("sql", "ERROR: sql connection failed"); | ||
| UnLock(); | ||
| return false; | ||
| } | ||
| catch (const std::exception& ex) | ||
| { | ||
| // ... | ||
| dbg_msg("sql", "1 %s",ex.what()); | ||
|
|
||
| } | ||
| catch (const std::string& ex) | ||
| { | ||
| // ... | ||
| dbg_msg("sql", "2 %s",ex.c_str()); | ||
| } | ||
| catch( int ) | ||
| { | ||
| dbg_msg("sql", "3 %s"); | ||
| } | ||
| catch( float ) | ||
| { | ||
| dbg_msg("sql", "4 %s"); | ||
| } | ||
|
|
||
| catch( char[] ) | ||
| { | ||
| dbg_msg("sql", "5 %s"); | ||
| } | ||
|
|
||
| catch( char ) | ||
| { | ||
| dbg_msg("sql", "6 %s"); | ||
| } | ||
| catch (...) | ||
| { | ||
| dbg_msg("sql", "Unknown Error cause by the MySQL/C++ Connector, my advice compile server_debug and use it"); | ||
|
|
||
| dbg_msg("sql", "ERROR: sql connection failed"); | ||
| UnLock(); | ||
| return false; | ||
| } | ||
| UnLock(); | ||
| return false; | ||
| } | ||
|
|
||
| void CSqlServer::Disconnect() | ||
| { | ||
| UnLock(); | ||
| } | ||
|
|
||
| void CSqlServer::CreateTables() | ||
| { | ||
| if (!Connect()) | ||
| return; | ||
|
|
||
| try | ||
| { | ||
| char aBuf[1024]; | ||
|
|
||
| // create tables | ||
| str_format(aBuf, sizeof(aBuf), "CREATE TABLE IF NOT EXISTS %s_race (Map VARCHAR(128) BINARY NOT NULL, Name VARCHAR(%d) BINARY NOT NULL, Timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP , Time FLOAT DEFAULT 0, Server CHAR(4), cp1 FLOAT DEFAULT 0, cp2 FLOAT DEFAULT 0, cp3 FLOAT DEFAULT 0, cp4 FLOAT DEFAULT 0, cp5 FLOAT DEFAULT 0, cp6 FLOAT DEFAULT 0, cp7 FLOAT DEFAULT 0, cp8 FLOAT DEFAULT 0, cp9 FLOAT DEFAULT 0, cp10 FLOAT DEFAULT 0, cp11 FLOAT DEFAULT 0, cp12 FLOAT DEFAULT 0, cp13 FLOAT DEFAULT 0, cp14 FLOAT DEFAULT 0, cp15 FLOAT DEFAULT 0, cp16 FLOAT DEFAULT 0, cp17 FLOAT DEFAULT 0, cp18 FLOAT DEFAULT 0, cp19 FLOAT DEFAULT 0, cp20 FLOAT DEFAULT 0, cp21 FLOAT DEFAULT 0, cp22 FLOAT DEFAULT 0, cp23 FLOAT DEFAULT 0, cp24 FLOAT DEFAULT 0, cp25 FLOAT DEFAULT 0, KEY (Map, Name)) CHARACTER SET utf8 ;", m_aPrefix, MAX_NAME_LENGTH); | ||
| executeSql(aBuf); | ||
|
|
||
| str_format(aBuf, sizeof(aBuf), "CREATE TABLE IF NOT EXISTS %s_teamrace (Map VARCHAR(128) BINARY NOT NULL, Name VARCHAR(%d) BINARY NOT NULL, Timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, Time FLOAT DEFAULT 0, ID VARBINARY(16) NOT NULL, KEY Map (Map)) CHARACTER SET utf8 ;", m_aPrefix, MAX_NAME_LENGTH); | ||
| executeSql(aBuf); | ||
|
|
||
| str_format(aBuf, sizeof(aBuf), "CREATE TABLE IF NOT EXISTS %s_maps (Map VARCHAR(128) BINARY NOT NULL, Server VARCHAR(32) BINARY NOT NULL, Mapper VARCHAR(128) BINARY NOT NULL, Points INT DEFAULT 0, Stars INT DEFAULT 0, Timestamp TIMESTAMP, UNIQUE KEY Map (Map)) CHARACTER SET utf8 ;", m_aPrefix); | ||
| executeSql(aBuf); | ||
|
|
||
| str_format(aBuf, sizeof(aBuf), "CREATE TABLE IF NOT EXISTS %s_saves (Savegame TEXT CHARACTER SET utf8 BINARY NOT NULL, Map VARCHAR(128) BINARY NOT NULL, Code VARCHAR(128) BINARY NOT NULL, Timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, Server CHAR(4), UNIQUE KEY (Map, Code)) CHARACTER SET utf8 ;", m_aPrefix); | ||
| executeSql(aBuf); | ||
|
|
||
| str_format(aBuf, sizeof(aBuf), "CREATE TABLE IF NOT EXISTS %s_points (Name VARCHAR(%d) BINARY NOT NULL, Points INT DEFAULT 0, UNIQUE KEY Name (Name)) CHARACTER SET utf8 ;", m_aPrefix, MAX_NAME_LENGTH); | ||
| executeSql(aBuf); | ||
|
|
||
| dbg_msg("sql", "Tables were created successfully"); | ||
| } | ||
| catch (sql::SQLException &e) | ||
| { | ||
| dbg_msg("sql", "MySQL Error: %s", e.what()); | ||
| } | ||
|
|
||
| Disconnect(); | ||
| } | ||
|
|
||
| void CSqlServer::executeSql(const char *pCommand) | ||
| { | ||
| m_pStatement->execute(pCommand); | ||
| } | ||
|
|
||
| void CSqlServer::executeSqlQuery(const char *pQuery) | ||
| { | ||
| if (m_pResults) | ||
| delete m_pResults; | ||
|
|
||
| // set it to 0, so exceptions raised from executeQuery can not make m_pResults point to invalid memory | ||
| m_pResults = 0; | ||
| m_pResults = m_pStatement->executeQuery(pQuery); | ||
| } | ||
|
|
||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| #ifndef ENGINE_SERVER_SQL_SERVER_H | ||
| #define ENGINE_SERVER_SQL_SERVER_H | ||
|
|
||
| #include <mysql_connection.h> | ||
|
|
||
| #include <cppconn/driver.h> | ||
| #include <cppconn/exception.h> | ||
| #include <cppconn/statement.h> | ||
|
|
||
| class CSqlServer | ||
| { | ||
| public: | ||
| CSqlServer(const char* pDatabase, const char* pPrefix, const char* pUser, const char* pPass, const char* pIp, int Port, bool ReadOnly = true, bool SetUpDb = false); | ||
| ~CSqlServer(); | ||
|
|
||
| bool Connect(); | ||
| void Disconnect(); | ||
| void CreateTables(); | ||
|
|
||
| void executeSql(const char* pCommand); | ||
| void executeSqlQuery(const char* pQuery); | ||
|
|
||
| sql::ResultSet* GetResults() { return m_pResults; } | ||
|
|
||
| const char* GetDatabase() { return m_aDatabase; } | ||
| const char* GetPrefix() { return m_aPrefix; } | ||
| const char* GetUser() { return m_aUser; } | ||
| const char* GetPass() { return m_aPass; } | ||
| const char* GetIP() { return m_aIp; } | ||
| int GetPort() { return m_Port; } | ||
|
|
||
| void Lock() { lock_wait(m_SqlLock); } | ||
| void UnLock() { lock_unlock(m_SqlLock); } | ||
|
|
||
| static int ms_NumReadServer; | ||
| static int ms_NumWriteServer; | ||
|
|
||
| private: | ||
| sql::Driver *m_pDriver; | ||
| sql::Connection *m_pConnection; | ||
| sql::Statement *m_pStatement; | ||
| sql::ResultSet *m_pResults; | ||
|
|
||
| // copy of config vars | ||
| char m_aDatabase[64]; | ||
| char m_aPrefix[64]; | ||
| char m_aUser[64]; | ||
| char m_aPass[64]; | ||
| char m_aIp[64]; | ||
| int m_Port; | ||
|
|
||
| bool m_SetUpDB; | ||
|
|
||
| LOCK m_SqlLock; | ||
| }; | ||
|
|
||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,145 @@ | ||
| #include <cmath> | ||
| #include <cstring> | ||
| #include <ctime> | ||
| #include <base/system.h> | ||
|
|
||
| #include "sql_string_helpers.h" | ||
|
|
||
| void sqlstr::FuzzyString(char *pString) | ||
| { | ||
| char newString[32*4-1]; | ||
| int pos = 0; | ||
|
|
||
| for(int i=0;i<64;i++) | ||
| { | ||
| if(!pString[i]) | ||
| break; | ||
|
|
||
| newString[pos++] = pString[i]; | ||
| if (pString[i] != '\\' && str_utf8_isstart(pString[i+1])) | ||
| newString[pos++] = '%'; | ||
| } | ||
|
|
||
| newString[pos] = '\0'; | ||
| strcpy(pString, newString); | ||
| } | ||
|
|
||
| // anti SQL injection | ||
| void sqlstr::ClearString(char *pString, int size) | ||
| { | ||
| char *newString = new char [size * 2 - 1]; | ||
| int pos = 0; | ||
|
|
||
| for(int i=0;i<size;i++) | ||
| { | ||
| if(pString[i] == '\\') | ||
| { | ||
| newString[pos++] = '\\'; | ||
| newString[pos++] = '\\'; | ||
| } | ||
| else if(pString[i] == '\'') | ||
| { | ||
| newString[pos++] = '\\'; | ||
| newString[pos++] = '\''; | ||
| } | ||
| else if(pString[i] == '"') | ||
| { | ||
| newString[pos++] = '\\'; | ||
| newString[pos++] = '"'; | ||
| } | ||
| else | ||
| { | ||
| newString[pos++] = pString[i]; | ||
| } | ||
| } | ||
|
|
||
| newString[pos] = '\0'; | ||
|
|
||
| strcpy(pString, newString); | ||
| delete [] newString; | ||
| } | ||
|
|
||
| void sqlstr::agoTimeToString(int agoTime, char agoString[]) | ||
| { | ||
| char aBuf[20]; | ||
| int times[7] = | ||
| { | ||
| 60 * 60 * 24 * 365 , | ||
| 60 * 60 * 24 * 30 , | ||
| 60 * 60 * 24 * 7, | ||
| 60 * 60 * 24 , | ||
| 60 * 60 , | ||
| 60 , | ||
| 1 | ||
| }; | ||
| char names[7][6] = | ||
| { | ||
| "year", | ||
| "month", | ||
| "week", | ||
| "day", | ||
| "hour", | ||
| "min", | ||
| "sec" | ||
| }; | ||
|
|
||
| int seconds = 0; | ||
| char name[6]; | ||
| int count = 0; | ||
| int i = 0; | ||
|
|
||
| // finding biggest match | ||
| for(i = 0; i<7; i++) | ||
| { | ||
| seconds = times[i]; | ||
| strcpy(name, names[i]); | ||
|
|
||
| count = floor((float)agoTime/(float)seconds); | ||
| if(count != 0) | ||
| { | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| if(count == 1) | ||
| { | ||
| str_format(aBuf, sizeof(aBuf), "%d %s", 1 , name); | ||
| } | ||
| else | ||
| { | ||
| str_format(aBuf, sizeof(aBuf), "%d %ss", count , name); | ||
| } | ||
| strcat(agoString, aBuf); | ||
|
|
||
| if (i + 1 < 7) | ||
| { | ||
| // getting second piece now | ||
| int seconds2 = times[i+1]; | ||
| char name2[6]; | ||
| strcpy(name2, names[i+1]); | ||
|
|
||
| // add second piece if it's greater than 0 | ||
| int count2 = floor((float)(agoTime - (seconds * count)) / (float)seconds2); | ||
|
|
||
| if (count2 != 0) | ||
| { | ||
| if(count2 == 1) | ||
| { | ||
| str_format(aBuf, sizeof(aBuf), " and %d %s", 1 , name2); | ||
| } | ||
| else | ||
| { | ||
| str_format(aBuf, sizeof(aBuf), " and %d %ss", count2 , name2); | ||
| } | ||
| strcat(agoString, aBuf); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| void sqlstr::getTimeStamp(char* dest, unsigned int size) | ||
| { | ||
| std::time_t rawtime; | ||
| std::time(&rawtime); | ||
|
|
||
| str_timestamp_ex(rawtime, dest, size, "%Y-%m-%d %H:%M:%S"); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| #ifndef ENGINE_SERVER_SQL_STRING_HELPERS_H | ||
| #define ENGINE_SERVER_SQL_STRING_HELPERS_H | ||
|
|
||
| namespace sqlstr | ||
| { | ||
|
|
||
| void FuzzyString(char *pString); | ||
|
|
||
| // anti SQL injection | ||
| void ClearString(char *pString, int size = 32); | ||
|
|
||
| void agoTimeToString(int agoTime, char agoString[]); | ||
|
|
||
| void getTimeStamp(char* dest, unsigned int size); | ||
|
|
||
|
|
||
| template<unsigned int size> | ||
| class CSqlString | ||
| { | ||
| public: | ||
| CSqlString() {} | ||
|
|
||
| CSqlString(const char* pStr) | ||
| { | ||
| str_copy(m_aString, pStr, size); | ||
| str_copy(m_aClearString, pStr, size); | ||
| ClearString(m_aClearString, sizeof(m_aClearString)); | ||
| } | ||
|
|
||
| const char* Str() const { return m_aString; } | ||
| const char* ClrStr() const { return m_aClearString; } | ||
|
|
||
| CSqlString& operator = (const char* pStr) | ||
| { | ||
| str_copy(m_aString, pStr, size); | ||
| str_copy(m_aClearString, pStr, size); | ||
| ClearString(m_aClearString, sizeof(m_aClearString)); | ||
| return *this; | ||
| } | ||
|
|
||
| private: | ||
| char m_aString[size]; | ||
| char m_aClearString[size * 2 - 1]; | ||
| }; | ||
|
|
||
| } | ||
|
|
||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| #include <base/system.h> | ||
| #include <base/tl/array.h> | ||
| #include <engine/shared/datafile.h> | ||
| #include <engine/storage.h> | ||
| #include <game/mapitems.h> | ||
|
|
||
| void CreateEmptyMap(IStorage *pStorage) | ||
| { | ||
| CDataFileWriter Writer; | ||
| if(!Writer.Open(pStorage, "maps/dummy3.map")) | ||
| { | ||
| dbg_msg("empty_map", "couldn't open map file 'dummy3.map' for writing"); | ||
| return; | ||
| } | ||
| CMapItemGroup_v1 Group; | ||
| Group.m_Version = 1; | ||
| Group.m_OffsetX = 0; | ||
| Group.m_OffsetY = 0; | ||
| Group.m_ParallaxX = 0; | ||
| Group.m_ParallaxY = 0; | ||
| Group.m_StartLayer = 0; | ||
| Group.m_NumLayers = 2; | ||
| Writer.AddItem(MAPITEMTYPE_GROUP, 0, sizeof(Group), &Group); | ||
|
|
||
| CMapItemLayerTilemap GameLayer; | ||
| GameLayer.m_Layer.m_Version = 0; // Not set by the official client. | ||
| GameLayer.m_Layer.m_Type = LAYERTYPE_TILES; | ||
| GameLayer.m_Layer.m_Flags = 0; | ||
| GameLayer.m_Version = 2; | ||
| GameLayer.m_Width = 2; | ||
| GameLayer.m_Height = 2; | ||
| GameLayer.m_Flags = TILESLAYERFLAG_GAME; | ||
| GameLayer.m_Color.r = 0; | ||
| GameLayer.m_Color.g = 0; | ||
| GameLayer.m_Color.b = 0; | ||
| GameLayer.m_Color.a = 0; | ||
| GameLayer.m_ColorEnv = -1; | ||
| GameLayer.m_ColorEnvOffset = 0; | ||
| GameLayer.m_Image = -1; | ||
| GameLayer.m_Data = 0; | ||
| Writer.AddItem(MAPITEMTYPE_LAYER, 0, sizeof(GameLayer) - sizeof(GameLayer.m_aName), &GameLayer); | ||
|
|
||
| CMapItemLayerTilemap Layer; | ||
| Layer.m_Layer.m_Version = 0; | ||
| Layer.m_Layer.m_Type = LAYERTYPE_TILES; | ||
| Layer.m_Layer.m_Flags = 0; | ||
| Layer.m_Version = 2; | ||
| Layer.m_Width = 2; | ||
| Layer.m_Height = 2; | ||
| Layer.m_Flags = 0; | ||
| Layer.m_Color.r = 0; | ||
| Layer.m_Color.g = 0; | ||
| Layer.m_Color.b = 0; | ||
| Layer.m_Color.a = 255; | ||
| Layer.m_ColorEnv = -1; | ||
| Layer.m_ColorEnvOffset = 0; | ||
| Layer.m_Image = -1; | ||
| Layer.m_Data = 0; | ||
| Writer.AddItem(MAPITEMTYPE_LAYER, 1, sizeof(Layer) - sizeof(Layer.m_aName), &Layer); | ||
|
|
||
| CTile Tiles[4]; | ||
| for(int i = 0; i < 4; i++) | ||
| { | ||
| Tiles[i].m_Index = 1; | ||
| Tiles[i].m_Flags = 0; | ||
| Tiles[i].m_Skip = 0; | ||
| Tiles[i].m_Reserved = 0; | ||
| } | ||
| Writer.AddData(sizeof(Tiles), &Tiles); | ||
|
|
||
| Writer.Finish(); | ||
| } | ||
|
|
||
| int main(int argc, const char **argv) | ||
| { | ||
| dbg_logger_stdout(); | ||
| IStorage *pStorage = CreateStorage("Teeworlds", IStorage::STORAGETYPE_SERVER, argc, argv); | ||
| CreateEmptyMap(pStorage); | ||
| return 0; | ||
| } |