From f68f683e22d31255cce845e4d86179f25bde09c2 Mon Sep 17 00:00:00 2001 From: Avery Date: Tue, 7 Dec 2021 18:14:15 -0800 Subject: [PATCH 01/11] Revert "separate AgSpaceStuff and AgShipShake" --- CMakeLists.txt | 2 +- dGame/Entity.cpp | 12 ++--- dScripts/AgShipShake.cpp | 93 --------------------------------------- dScripts/AgShipShake.h | 18 -------- dScripts/AgSpaceStuff.cpp | 74 +++++++++++++++++++++++++++++++ dScripts/AgSpaceStuff.h | 10 +++++ dScripts/CppScripts.cpp | 5 +-- 7 files changed, 89 insertions(+), 125 deletions(-) delete mode 100644 dScripts/AgShipShake.cpp delete mode 100644 dScripts/AgShipShake.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 9789aff8..cf7988c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,7 +95,7 @@ if (__include_backtrace__ AND __compile_backtrace__) link_directories(${backtrace_SOURCE_DIR}/.libs/) include_directories(${backtrace_SOURCE_DIR}) -endif(__include_backtrace__ AND __compile_backtrace__) +endif(__include_backtrace__) endif() endif(UNIX) diff --git a/dGame/Entity.cpp b/dGame/Entity.cpp index 1d31eb8d..c98323a9 100644 --- a/dGame/Entity.cpp +++ b/dGame/Entity.cpp @@ -1222,19 +1222,13 @@ void Entity::UpdateXMLDoc(tinyxml2::XMLDocument* doc) { } void Entity::Update(const float deltaTime) { - int timerSize = m_Timers.size(); - for (int i = 0; i < timerSize; i++) { + for (int i = 0; i < m_Timers.size(); i++) { m_Timers[i]->Update(deltaTime); if (m_Timers[i]->GetTime() <= 0) { const auto timerName = m_Timers[i]->GetName(); - do { //sometimes, due to a race condition, m_Timers.erase doesn't actually erase, repeat until it does - if (m_Timers[i]->GetName() != timerName) { - break; - } - delete m_Timers[i]; - m_Timers.erase(m_Timers.begin() + i); - } while (m_Timers.size() == timerSize); //timer size indicates whether it's actually successfully been erased or not + delete m_Timers[i]; + m_Timers.erase(m_Timers.begin() + i); for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) { script->OnTimerDone(this, timerName); diff --git a/dScripts/AgShipShake.cpp b/dScripts/AgShipShake.cpp deleted file mode 100644 index 643bc695..00000000 --- a/dScripts/AgShipShake.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include "AgShipShake.h" -#include "GeneralUtils.h" -#include "GameMessages.h" -#include "dZoneManager.h" -#include "EntityManager.h" -#include "Game.h" - -void AgShipShake::OnStartup(Entity* self) { - EntityInfo info{}; - - info.pos = { -418, 585, -30 }; - info.lot = 33; - info.spawnerID = self->GetObjectID(); - - auto* ref = EntityManager::Instance()->CreateEntity(info); - - EntityManager::Instance()->ConstructEntity(ref); - - self->SetVar(u"ShakeObject", ref->GetObjectID()); - - self->AddTimer("ShipShakeIdle", 1.0f); - self->SetVar(u"RandomTime", 10); -} - -void AgShipShake::OnTimerDone(Entity* self, std::string timerName) { - if (timerName == "ShipShakeExplode") { - DoShake(self, true); - } - else if (timerName == "ShipShakeIdle") { - DoShake(self, false); - } -} - -void AgShipShake::DoShake(Entity* self, bool explodeIdle) { - if (!explodeIdle) { - auto* ref = EntityManager::Instance()->GetEntity(self->GetVar(u"ShakeObject")); - - const auto randomTime = self->GetVar(u"RandomTime"); - auto time = GeneralUtils::GenerateRandomNumber(0, randomTime + 1); - - if (time < randomTime / 2) { - time += randomTime / 2; - } - - self->AddTimer("ShipShakeIdle", static_cast(time)); - - if (ref) - GameMessages::SendPlayEmbeddedEffectOnAllClientsNearObject(ref, FXName, ref->GetObjectID(), 500.0f); - - auto* debrisObject = GetEntityInGroup(DebrisFX); - - if (debrisObject) - GameMessages::SendPlayFXEffect(debrisObject, -1, u"DebrisFall", "Debris", LWOOBJID_EMPTY, 1.0f, 1.0f, true); - - const auto randomFx = GeneralUtils::GenerateRandomNumber(0, 3); - - auto* shipFxObject = GetEntityInGroup(ShipFX); - if (shipFxObject) { - std::string effectType = "shipboom" + std::to_string(randomFx); - GameMessages::SendPlayFXEffect(shipFxObject, 559, GeneralUtils::ASCIIToUTF16(effectType), "FX", LWOOBJID_EMPTY, 1.0f, 1.0f, true); - } - - self->AddTimer("ShipShakeExplode", 5.0f); - - auto* shipFxObject2 = GetEntityInGroup(ShipFX2); - if (shipFxObject2) - GameMessages::SendPlayAnimation(shipFxObject2, u"explosion"); - } - else { - auto* shipFxObject = GetEntityInGroup(ShipFX); - auto* shipFxObject2 = GetEntityInGroup(ShipFX2); - - if (shipFxObject) - GameMessages::SendPlayAnimation(shipFxObject, u"idle"); - - if (shipFxObject2) - GameMessages::SendPlayAnimation(shipFxObject2, u"idle"); - } -} - -Entity* AgShipShake::GetEntityInGroup(const std::string& group) { - auto entities = EntityManager::Instance()->GetEntitiesInGroup(group); - Entity* en = nullptr; - - for (auto entity : entities) { - if (entity) { - en = entity; - break; - } - } - - return en; -} diff --git a/dScripts/AgShipShake.h b/dScripts/AgShipShake.h deleted file mode 100644 index b1e1b557..00000000 --- a/dScripts/AgShipShake.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once -#include "CppScripts.h" - -class AgShipShake : public CppScripts::Script { -public: - void OnStartup(Entity* self); - void OnTimerDone(Entity* self, std::string timerName); - void DoShake(Entity* self, bool explodeIdle); - - std::string DebrisFX = "DebrisFX"; - std::string ShipFX = "ShipFX"; - std::string ShipFX2 = "ShipFX2"; - std::u16string FXName = u"camshake-bridge"; - -private: - Entity* GetEntityInGroup(const std::string& group); -}; - diff --git a/dScripts/AgSpaceStuff.cpp b/dScripts/AgSpaceStuff.cpp index 1e82237e..2550f001 100644 --- a/dScripts/AgSpaceStuff.cpp +++ b/dScripts/AgSpaceStuff.cpp @@ -1,6 +1,7 @@ #include "AgSpaceStuff.h" #include "GeneralUtils.h" #include "GameMessages.h" +#include "dZoneManager.h" #include "EntityManager.h" #include "Game.h" @@ -16,6 +17,11 @@ void AgSpaceStuff::OnStartup(Entity* self) { auto* ref = EntityManager::Instance()->CreateEntity(info); EntityManager::Instance()->ConstructEntity(ref); + + self->SetVar(u"ShakeObject", ref->GetObjectID()); + + self->AddTimer("ShipShakeIdle", 1.0f); + self->SetVar(u"RandomTime", 10); } void AgSpaceStuff::OnTimerDone(Entity* self, std::string timerName) { @@ -32,4 +38,72 @@ void AgSpaceStuff::OnTimerDone(Entity* self, std::string timerName) { GameMessages::SendPlayAnimation(self, u"path_0" + (GeneralUtils::to_u16string(pathType))); self->AddTimer("FloaterScale", randTime); } + else if (timerName == "ShipShakeExplode") { + DoShake(self, true); + } + else if (timerName == "ShipShakeIdle") { + DoShake(self, false); + } +} + +void AgSpaceStuff::DoShake(Entity* self, bool explodeIdle) { + + if (!explodeIdle) { + auto* ref = EntityManager::Instance()->GetEntity(self->GetVar(u"ShakeObject")); + + const auto randomTime = self->GetVar(u"RandomTime"); + auto time = GeneralUtils::GenerateRandomNumber(0, randomTime + 1); + + if (time < randomTime / 2) { + time += randomTime / 2; + } + + self->AddTimer("ShipShakeIdle", static_cast(time)); + + if (ref) + GameMessages::SendPlayEmbeddedEffectOnAllClientsNearObject(ref, FXName, ref->GetObjectID(), 500.0f); + + auto* debrisObject = GetEntityInGroup(DebrisFX); + + if (debrisObject) + GameMessages::SendPlayFXEffect(debrisObject, -1, u"DebrisFall", "Debris", LWOOBJID_EMPTY, 1.0f, 1.0f, true); + + const auto randomFx = GeneralUtils::GenerateRandomNumber(0, 3); + + auto* shipFxObject = GetEntityInGroup(ShipFX); + if (shipFxObject) { + std::string effectType = "shipboom" + std::to_string(randomFx); + GameMessages::SendPlayFXEffect(shipFxObject, 559, GeneralUtils::ASCIIToUTF16(effectType), "FX", LWOOBJID_EMPTY, 1.0f, 1.0f, true); + } + + self->AddTimer("ShipShakeExplode", 5.0f); + + auto* shipFxObject2 = GetEntityInGroup(ShipFX2); + if (shipFxObject2) + GameMessages::SendPlayAnimation(shipFxObject2, u"explosion"); + } + else { + auto* shipFxObject = GetEntityInGroup(ShipFX); + auto* shipFxObject2 = GetEntityInGroup(ShipFX2); + + if (shipFxObject) + GameMessages::SendPlayAnimation(shipFxObject, u"idle"); + + if (shipFxObject2) + GameMessages::SendPlayAnimation(shipFxObject2, u"idle"); + } +} + +Entity* AgSpaceStuff::GetEntityInGroup(const std::string& group) { + auto entities = EntityManager::Instance()->GetEntitiesInGroup(group); + Entity* en = nullptr; + + for (auto entity : entities) { + if (entity) { + en = entity; + break; + } + } + + return en; } diff --git a/dScripts/AgSpaceStuff.h b/dScripts/AgSpaceStuff.h index 13b29f1c..8d816691 100644 --- a/dScripts/AgSpaceStuff.h +++ b/dScripts/AgSpaceStuff.h @@ -5,4 +5,14 @@ class AgSpaceStuff : public CppScripts::Script { public: void OnStartup(Entity* self); void OnTimerDone(Entity* self, std::string timerName); + void DoShake(Entity* self, bool explodeIdle); + + std::string DebrisFX = "DebrisFX"; + std::string ShipFX = "ShipFX"; + std::string ShipFX2 = "ShipFX2"; + std::u16string FXName = u"camshake-bridge"; + +private: + Entity* GetEntityInGroup(const std::string& group); }; + diff --git a/dScripts/CppScripts.cpp b/dScripts/CppScripts.cpp index a07d4f52..99702805 100644 --- a/dScripts/CppScripts.cpp +++ b/dScripts/CppScripts.cpp @@ -15,7 +15,6 @@ //VE / AG scripts: #include "AgShipPlayerDeathTrigger.h" #include "AgShipPlayerShockServer.h" -#include "AgShipShake.h" #include "AgSpaceStuff.h" #include "AgImagSmashable.h" #include "NpcNpSpacemanBob.h" @@ -291,9 +290,7 @@ CppScripts::Script* CppScripts::GetScript(Entity* parent, const std::string& scr script = new AgShipPlayerDeathTrigger(); else if (scriptName == "scripts\\ai\\NP\\L_NPC_NP_SPACEMAN_BOB.lua") script = new NpcNpSpacemanBob(); - else if (scriptName == "scripts\\ai\\AG\\L_AG_SHIP_SHAKE.lua") - script = new AgShipShake(); - else if (scriptName == "scripts\\ai\\AG\\L_AG_SPACE_STUFF.lua") + else if (scriptName == "scripts\\ai\\AG\\L_AG_SPACE_STUFF.lua") // Broken, will (sometimes) display all animations at once on initial login script = new AgSpaceStuff(); else if (scriptName == "scripts\\ai\\AG\\L_AG_SHIP_PLAYER_SHOCK_SERVER.lua") script = new AgShipPlayerShockServer(); From 59f053433c2cedf6176d1ba719cdaaffea8d13f5 Mon Sep 17 00:00:00 2001 From: Jack Kawell Date: Tue, 7 Dec 2021 23:33:34 -0700 Subject: [PATCH 02/11] External IP configuration as well as other small improvements --- .env.example | 9 ++++++++- docker-compose.yml | 5 ++++- docker/Dockerfile | 6 ++++-- docker/start_server.sh | 18 ++++++++---------- 4 files changed, 24 insertions(+), 14 deletions(-) diff --git a/.env.example b/.env.example index 5deedba1..22fff590 100644 --- a/.env.example +++ b/.env.example @@ -5,4 +5,11 @@ BUILD_THREADS=1 # Updates NET_VERSION in CMakeVariables.txt BUILD_VERSION=171022 # make sure this is a long random string -ACCOUNT_MANAGER_SECRET= \ No newline at end of file +ACCOUNT_MANAGER_SECRET= +# Should be the externally facing IP of your server host +EXTERNAL_IP=localhost +# Database values +MARIADB_USER=SECRET_VALUE_CHANGE_ME +MARIADB_PASSWORD=SECRET_VALUE_CHANGE_ME +MARIADB_ROOT_PASSWORD=SECRET_VALUE_CHANGE_ME +MARIADB_DATABASE=SECRET_VALUE_CHANGE_ME \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 68fa6302..7edbebbc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,6 +14,8 @@ services: - ./migrations/dlu:/docker-entrypoint-initdb.d networks: - darkflame + ports: + - 3306:3306 darkflame: container_name: DarkFlameServer @@ -30,6 +32,7 @@ services: - DATABASE_HOST=database - DATABASE_USER=${MARIADB_USER:-darkflame} - DATABASE_PASSWORD=${MARIADB_PASSWORD:-darkflame} + - EXTERNAL_IP=${EXTERNAL_IP:-darkflame} volumes: - ${CLIENT_PATH:?err}:/client depends_on: @@ -82,6 +85,6 @@ services: networks: darkflame: - + volumes: database: diff --git a/docker/Dockerfile b/docker/Dockerfile index 6b2b3712..8f583b4b 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -33,7 +33,7 @@ ARG BUILD_VERSION=171022 RUN echo "Build server" && \ mkdir -p build && \ cd build && \ - sed -i -e "s/171022/${BUILD_VERSION}/g" ../CMakeVariables.txt && \ + sed -i -e "s/171022/${BUILD_VERSION}/g" ../CMakeVariables.txt && \ cmake .. && \ make -j $BUILD_THREADS @@ -41,7 +41,7 @@ RUN unzip /build/resources/navmeshes.zip -d /build/build/res/maps FROM gcc:11 as runtime -RUN --mount=type=cache,id=runtime-apt-cache,target=/var/cache/apt \ +RUN --mount=type=cache,id=runtime-apt-cache,target=/var/cache/apt \ apt update && \ apt install mariadb-client python3 sudo sqlite3 -yqq --no-install-recommends && \ apt remove -y libmysqlcppconn7v5 libmysqlcppconn-dev && \ @@ -61,4 +61,6 @@ COPY docker/start_server.sh /start_server.sh RUN chmod +x /start_server.sh +RUN mkdir /app/logs + CMD [ "/start_server.sh" ] \ No newline at end of file diff --git a/docker/start_server.sh b/docker/start_server.sh index 975646b0..85d1509b 100644 --- a/docker/start_server.sh +++ b/docker/start_server.sh @@ -12,6 +12,7 @@ function set_defaults() { CHAT_SERVER_PORT=${CHAT_SERVER_PORT:-2005} MAX_CLIENTS=${MAX_CLIENTS:-999} + EXTERNAL_IP=${EXTERNAL_IP:-localhost} echo "Start server with configuration:" echo "===== Database Config =========" @@ -23,6 +24,7 @@ function set_defaults() { echo "===== Other settings ==========" echo "Chat server port: $CHAT_SERVER_PORT" echo "Max clients: $MAX_CLIENTS" + echo "External IP: $EXTERNAL_IP" } function check_sql_connection() { @@ -46,7 +48,7 @@ function update_database_ini_values_for() { update_ini $INI_FILE mysql_username $DATABASE_USER update_ini $INI_FILE mysql_password $DATABASE_PASSWORD if [[ "$INI_FILE" != "worldconfig.ini" ]]; then - update_ini $INI_FILE external_ip "darkflame" + update_ini $INI_FILE external_ip $EXTERNAL_IP fi } @@ -64,29 +66,25 @@ function symlink_client_files() { ln -s /client/client/res/macros/ /app/res/macros ln -s /client/client/res/BrickModels/ /app/res/BrickModels ln -s /client/client/res/chatplus_en_us.txt /app/res/chatplus_en_us.txt + ln -s /client/client/res/maps/ /app/res/maps ln -s /client/client/res/names/ /app/res/names ln -s /client/client/locale/locale.xml /app/locale/locale.xml - ( - cd /client/client/res/maps - readarray -d '' entries < <(printf '%s\0' * | sort -zV) - for entry in "${entries[@]}"; do - ln -s /client/client/res/maps/$entry /app/res/maps/ - done - ) } function fdb_to_sqlite() { echo "Run fdb_to_sqlite" - python3 /app/utils/fdb_to_sqlite.py /client/client/res/CDClient.fdb --sqlite_path /app/res/CDServer.sqlite + python3 /app/utils/fdb_to_sqlite.py /client/client/res/cdclient.fdb --sqlite_path /client/client/res/CDServer.sqlite ( cd /app/migrations/cdserver readarray -d '' entries < <(printf '%s\0' *.sql | sort -zV) for entry in "${entries[@]}"; do echo "Execute $entry" - sqlite3 /app/res/CDServer.sqlite < $entry + sqlite3 /client/client/res/CDServer.sqlite < $entry done ) + + ln -s /client/client/res/CDServer.sqlite /app/res/CDServer.sqlite } set_defaults From a38765d02d29ace4e89f640dae53abfb05d65471 Mon Sep 17 00:00:00 2001 From: Jack Kawell Date: Tue, 7 Dec 2021 23:44:37 -0700 Subject: [PATCH 03/11] fixing my mistakes (aka TheNoim is smarter than me) --- docker-compose.yml | 2 +- docker/start_server.sh | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 7edbebbc..9b60cd51 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,7 +15,7 @@ services: networks: - darkflame ports: - - 3306:3306 + - 3306:3306 # (optional) ports only exposed so that DB management tools can connect darkflame: container_name: DarkFlameServer diff --git a/docker/start_server.sh b/docker/start_server.sh index 85d1509b..50422c60 100644 --- a/docker/start_server.sh +++ b/docker/start_server.sh @@ -66,9 +66,16 @@ function symlink_client_files() { ln -s /client/client/res/macros/ /app/res/macros ln -s /client/client/res/BrickModels/ /app/res/BrickModels ln -s /client/client/res/chatplus_en_us.txt /app/res/chatplus_en_us.txt - ln -s /client/client/res/maps/ /app/res/maps ln -s /client/client/res/names/ /app/res/names ln -s /client/client/locale/locale.xml /app/locale/locale.xml + # need to iterate over entries in maps due to maps already being a directory with navmeshes/ in it + ( + cd /client/client/res/maps + readarray -d '' entries < <(printf '%s\0' * | sort -zV) + for entry in "${entries[@]}"; do + ln -s /client/client/res/maps/$entry /app/res/maps/ + done + ) } function fdb_to_sqlite() { From f185c8b5dbcde56879a6ddf0582c617ef38c6439 Mon Sep 17 00:00:00 2001 From: Jack Kawell Date: Wed, 8 Dec 2021 00:50:15 -0700 Subject: [PATCH 04/11] Moved all the setup of client files into a single use Docker image --- .gitignore | 1 + Docker.md | 19 +++---- docker-compose.yml | 22 +++++--- docker/Dockerfile | 11 +--- docker/setup.Dockerfile | 14 +++++ docker/setup.sh | 82 ++++++++++++++++++++++++++++ docker/start_server.sh | 115 +++++----------------------------------- 7 files changed, 137 insertions(+), 127 deletions(-) create mode 100644 docker/setup.Dockerfile create mode 100755 docker/setup.sh mode change 100644 => 100755 docker/start_server.sh diff --git a/.gitignore b/.gitignore index a28f0302..28f8f297 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ temp/ cmake-build-debug/ RelWithDebInfo/ +docker/configs # Third party libraries thirdparty/mysql/ diff --git a/Docker.md b/Docker.md index 9baf2add..06beb87e 100644 --- a/Docker.md +++ b/Docker.md @@ -1,22 +1,23 @@ # Run the Darkflame Server inside Docker -### What you need +## What you need - Docker (Docker Desktop or on Linux normal Docker) - Docker-Compose (Included in Docker Desktop) - LEGO® Universe Client -### Run server inside Docker +## Run server inside Docker 1. Copy `.env.example` and save it as `.env` inside the root directory of this repository 2. Edit the `.env` file and add your path to your LEGO® Universe Client after `CLIENT_PATH=` -3. Add some random long string after `ACCOUNT_MANAGER_SECRET=` in the `.env` file -4. (Optional) You can decrease the build time if you change number behind `BUILD_THREADS=` in the `.env` file. You should change it to the number of threads your system have. -5. Run `docker compose up -d --build` or `docker-compose up -d --build` and wait for it to complete -6. Now you can see the output of the server with `docker compose logs -f --tail 100` or `docker-compose logs -f --tail 100`. This can help you understand issues and there you can also see when the server finishes it's startup. +3. Update other values in the `.env` file as need (be sure to update passwords!) +4. Run `docker-compose up setup --build` +5. Run `docker-compose up -d database` +6. Run `docker-compose up -d account-manager brickbuildfix --build` +7. Run `docker-compose up -d darkflame` +8. Now you can see the output of the server with `docker compose logs -f --tail 100` or `docker-compose logs -f --tail 100`. This can help you understand issues and there you can also see when the server finishes it's startup. - -### Disable brickbuildfix +## Disable brickbuildfix If you don't need the http server running on port 80 do this: @@ -30,4 +31,4 @@ services: - donotstart ``` -4. Now run `docker compose up -d --build` or `docker-compose up -d --build` \ No newline at end of file +3. Now run `docker compose up -d --build` or `docker-compose up -d --build` \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 9b60cd51..3974a959 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,21 @@ version: "3" services: + setup: + container_name: DarkflameSetup + build: + context: . + dockerfile: ./docker/setup.Dockerfile + environment: + - DATABASE=${MARIADB_DATABASE:-darkflame} + - DATABASE_HOST=database + - DATABASE_USER=${MARIADB_USER:-darkflame} + - DATABASE_PASSWORD=${MARIADB_PASSWORD:-darkflame} + - EXTERNAL_IP=${EXTERNAL_IP:-darkflame} + volumes: + - ${CLIENT_PATH:?err}:/client + - ./docker/:/docker/ + database: container_name: DarkflameDatabase image: mariadb:10.6 @@ -27,14 +42,9 @@ services: args: - BUILD_THREADS=${BUILD_THREADS:-1} - BUILD_VERSION=${BUILD_VERSION:-171022} - environment: - - DATABASE=${MARIADB_DATABASE:-darkflame} - - DATABASE_HOST=database - - DATABASE_USER=${MARIADB_USER:-darkflame} - - DATABASE_PASSWORD=${MARIADB_PASSWORD:-darkflame} - - EXTERNAL_IP=${EXTERNAL_IP:-darkflame} volumes: - ${CLIENT_PATH:?err}:/client + - ./docker/configs/:/configs depends_on: - database ports: diff --git a/docker/Dockerfile b/docker/Dockerfile index 8f583b4b..0e5fa06a 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -43,7 +43,7 @@ FROM gcc:11 as runtime RUN --mount=type=cache,id=runtime-apt-cache,target=/var/cache/apt \ apt update && \ - apt install mariadb-client python3 sudo sqlite3 -yqq --no-install-recommends && \ + apt install sudo -yqq --no-install-recommends && \ apt remove -y libmysqlcppconn7v5 libmysqlcppconn-dev && \ rm -rf /var/lib/apt/lists/* @@ -51,16 +51,9 @@ WORKDIR /app COPY --from=build /build/build /app -COPY --from=build /build/migrations /app/migrations - +RUN mkdir /app/logs RUN mkdir -p /build/build && ln -s /app/_deps /build/build/_deps -ADD docker/*.py /app/utils/ - COPY docker/start_server.sh /start_server.sh -RUN chmod +x /start_server.sh - -RUN mkdir /app/logs - CMD [ "/start_server.sh" ] \ No newline at end of file diff --git a/docker/setup.Dockerfile b/docker/setup.Dockerfile new file mode 100644 index 00000000..b49f8dbf --- /dev/null +++ b/docker/setup.Dockerfile @@ -0,0 +1,14 @@ +FROM python:3.10.0-slim-buster as prep + +RUN apt update && apt install unzip sqlite3 + +WORKDIR /setup + +# copy needed files from repo +COPY resources/ resources/ +COPY migrations/cdserver/ migrations/cdserver +ADD docker/*.py utils/ + +COPY docker/setup.sh /setup.sh + +CMD [ "/setup.sh" ] \ No newline at end of file diff --git a/docker/setup.sh b/docker/setup.sh new file mode 100755 index 00000000..98a92515 --- /dev/null +++ b/docker/setup.sh @@ -0,0 +1,82 @@ +#!/bin/bash + +function update_ini() { + FILE="/docker/configs/$1" + KEY=$2 + NEW_VALUE=$3 + sed -i "/^$KEY=/s/=.*/=$NEW_VALUE/" $FILE +} + +function update_database_ini_values_for() { + INI_FILE=$1 + + update_ini $INI_FILE mysql_host $DATABASE_HOST + update_ini $INI_FILE mysql_database $DATABASE + update_ini $INI_FILE mysql_username $DATABASE_USER + update_ini $INI_FILE mysql_password $DATABASE_PASSWORD + if [[ "$INI_FILE" != "worldconfig.ini" ]]; then + update_ini $INI_FILE external_ip $EXTERNAL_IP + fi +} + +function update_ini_values() { + echo "Copying and updating config files" + + mkdir -p /docker/configs + cp resources/masterconfig.ini /docker/configs/ + cp resources/authconfig.ini /docker/configs/ + cp resources/chatconfig.ini /docker/configs/ + cp resources/worldconfig.ini /docker/configs/ + + update_ini worldconfig.ini chat_server_port $CHAT_SERVER_PORT + update_ini worldconfig.ini max_clients $MAX_CLIENTS + + update_database_ini_values_for masterconfig.ini + update_database_ini_values_for authconfig.ini + update_database_ini_values_for chatconfig.ini + update_database_ini_values_for worldconfig.ini +} + +function fdb_to_sqlite() { + echo "Run fdb_to_sqlite" + python3 utils/fdb_to_sqlite.py /client/client/res/cdclient.fdb --sqlite_path /client/client/res/CDServer.sqlite + + ( + cd migrations/cdserver + readarray -d '' entries < <(printf '%s\0' *.sql | sort -zV) + for entry in "${entries[@]}"; do + echo "Execute $entry" + sqlite3 /client/client/res/CDServer.sqlite < $entry + done + ) +} + +update_ini_values + +if [[ ! -d "/client" ]]; then + echo "Client not found." + echo "Did you forgot to mount the client into the \"/client\" directory?" + exit 1 +fi + +if [[ ! -f "/client/extracted" ]]; then + echo "Start client resource extraction" + + python3 utils/pkextractor.py /client/ /client/ + + touch /client/extracted +else + echo "Client already extracted. Skip this step" + echo "If you want to force re-extract, just delete the file called \"extracted\" in the client directory" +fi + +if [[ ! -f "/client/migrated" ]]; then + echo "Start client db migration" + + fdb_to_sqlite + + touch /client/migrated +else + echo "Client db already migrated. Skip this step" + echo "If you want to force re-migrate, just delete the file called \"migrated\" in the client directory" +fi diff --git a/docker/start_server.sh b/docker/start_server.sh old mode 100644 new mode 100755 index 50422c60..ee5de738 --- a/docker/start_server.sh +++ b/docker/start_server.sh @@ -1,72 +1,12 @@ #!/bin/bash -function set_defaults() { - DATABASE_PORT=${DATABASE_PORT:-3306} - DATABASE=${DATABASE:-darkflame} - - if [[ -z $DATABASE_PASSWORD ]]; then - USE_DATABASE_PASSWORD="No" - else - USE_DATABASE_PASSWORD="Yes" - fi - - CHAT_SERVER_PORT=${CHAT_SERVER_PORT:-2005} - MAX_CLIENTS=${MAX_CLIENTS:-999} - EXTERNAL_IP=${EXTERNAL_IP:-localhost} - - echo "Start server with configuration:" - echo "===== Database Config =========" - echo "Database: $DATABASE" - echo "Database host: $DATABASE_HOST" - echo "Database port: $DATABASE_PORT" - echo "Database user: $DATABASE_USER" - echo "Database password set: $USE_DATABASE_PASSWORD" - echo "===== Other settings ==========" - echo "Chat server port: $CHAT_SERVER_PORT" - echo "Max clients: $MAX_CLIENTS" - echo "External IP: $EXTERNAL_IP" -} - -function check_sql_connection() { - until echo '\q' | mysql -h"$DATABASE_HOST" -P"$DATABASE_PORT" -u"$DATABASE_USER" -p"$DATABASE_PASSWORD" $DATABASE; do - >&2 echo "MySQL/MariaDB is unavailable - sleeping" - sleep 1 - done -} - -function update_ini() { - INI_FILE=$1 - KEY=$2 - NEW_VALUE=$3 - sed -i "/^$KEY=/s/=.*/=$NEW_VALUE/" $INI_FILE -} - -function update_database_ini_values_for() { - INI_FILE=$1 - update_ini $INI_FILE mysql_host $DATABASE_HOST - update_ini $INI_FILE mysql_database $DATABASE - update_ini $INI_FILE mysql_username $DATABASE_USER - update_ini $INI_FILE mysql_password $DATABASE_PASSWORD - if [[ "$INI_FILE" != "worldconfig.ini" ]]; then - update_ini $INI_FILE external_ip $EXTERNAL_IP - fi -} - -function update_ini_values() { - update_ini worldconfig.ini chat_server_port $CHAT_SERVER_PORT - update_ini worldconfig.ini max_clients $MAX_CLIENTS - - update_database_ini_values_for masterconfig.ini - update_database_ini_values_for authconfig.ini - update_database_ini_values_for chatconfig.ini - update_database_ini_values_for worldconfig.ini -} - function symlink_client_files() { + echo "Creating symlinks for client files" ln -s /client/client/res/macros/ /app/res/macros ln -s /client/client/res/BrickModels/ /app/res/BrickModels ln -s /client/client/res/chatplus_en_us.txt /app/res/chatplus_en_us.txt ln -s /client/client/res/names/ /app/res/names + ln -s /client/client/res/CDServer.sqlite /app/res/CDServer.sqlite ln -s /client/client/locale/locale.xml /app/locale/locale.xml # need to iterate over entries in maps due to maps already being a directory with navmeshes/ in it ( @@ -78,51 +18,20 @@ function symlink_client_files() { ) } -function fdb_to_sqlite() { - echo "Run fdb_to_sqlite" - python3 /app/utils/fdb_to_sqlite.py /client/client/res/cdclient.fdb --sqlite_path /client/client/res/CDServer.sqlite - - ( - cd /app/migrations/cdserver - readarray -d '' entries < <(printf '%s\0' *.sql | sort -zV) - for entry in "${entries[@]}"; do - echo "Execute $entry" - sqlite3 /client/client/res/CDServer.sqlite < $entry - done - ) - - ln -s /client/client/res/CDServer.sqlite /app/res/CDServer.sqlite +function symlink_config_files() { + echo "Creating symlinks for config files" + rm /app/*.ini + ln -s /configs/authconfig.ini /app/authconfig.ini + ln -s /configs/chatconfig.ini /app/chatconfig.ini + ln -s /configs/masterconfig.ini /app/masterconfig.ini + ln -s /configs/worldconfig.ini /app/worldconfig.ini } -set_defaults - -check_sql_connection - -update_ini_values - -if [[ ! -d "/client" ]]; then - echo "Client not found." - echo "Did you forgot to mount the client into the \"/client\" directory?" - exit 1 -fi - -if [[ ! -f "/client/extracted" ]]; then - echo "Start client resource extraction" - - python3 /app/utils/pkextractor.py /client/ /client/ - - touch /client/extracted -else - echo "Client already extracted. Skip this step" - echo "If you want to force re-extract, just delete the file called \"extracted\" in the client directory" -fi - +# setup symlinks for volume files symlink_client_files +symlink_config_files -fdb_to_sqlite - +# start the server echo "Start MasterServer" - ./MasterServer - tail -f /dev/null \ No newline at end of file From 293aaff2a5788c567159357cfad663a290d893e0 Mon Sep 17 00:00:00 2001 From: Jack Kawell Date: Wed, 8 Dec 2021 01:54:44 -0700 Subject: [PATCH 05/11] A few more improvements --- Docker.md | 6 +++--- docker-compose.yml | 6 +++--- docker/start_server.sh | 12 +++++++++--- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/Docker.md b/Docker.md index 06beb87e..0e6ae3d7 100644 --- a/Docker.md +++ b/Docker.md @@ -11,10 +11,10 @@ 1. Copy `.env.example` and save it as `.env` inside the root directory of this repository 2. Edit the `.env` file and add your path to your LEGO® Universe Client after `CLIENT_PATH=` 3. Update other values in the `.env` file as need (be sure to update passwords!) -4. Run `docker-compose up setup --build` +4. Run `docker-compose up --build setup` 5. Run `docker-compose up -d database` -6. Run `docker-compose up -d account-manager brickbuildfix --build` -7. Run `docker-compose up -d darkflame` +6. Run `docker-compose up --build -d account-manager brickbuildfix` +7. Run `docker-compose up --build -d darkflame` 8. Now you can see the output of the server with `docker compose logs -f --tail 100` or `docker-compose logs -f --tail 100`. This can help you understand issues and there you can also see when the server finishes it's startup. ## Disable brickbuildfix diff --git a/docker-compose.yml b/docker-compose.yml index 3974a959..519fcce3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -33,7 +33,7 @@ services: - 3306:3306 # (optional) ports only exposed so that DB management tools can connect darkflame: - container_name: DarkFlameServer + container_name: DarkflameServer networks: - darkflame build: @@ -54,7 +54,7 @@ services: - "3000-3300:3000-3300/udp" brickbuildfix: - container_name: DarkFlameBrickBuildFix + container_name: DarkflameBrickBuildFix networks: - darkflame build: @@ -70,7 +70,7 @@ services: start_period: 40s account-manager: - container_name: DarkFlameAccountManager + container_name: DarkflameAccountManager networks: - darkflame build: diff --git a/docker/start_server.sh b/docker/start_server.sh index ee5de738..9a2345c6 100755 --- a/docker/start_server.sh +++ b/docker/start_server.sh @@ -27,9 +27,15 @@ function symlink_config_files() { ln -s /configs/worldconfig.ini /app/worldconfig.ini } -# setup symlinks for volume files -symlink_client_files -symlink_config_files +if [[ ! -f "/app/initialized" ]]; then + # setup symlinks for volume files + symlink_client_files + symlink_config_files + # do not run symlinks more than once + touch /app/initialized +else + echo "Server already initialized" +fi # start the server echo "Start MasterServer" From 51a9f61e888641d0f2daa7310e47fb4a24abf764 Mon Sep 17 00:00:00 2001 From: Nils Bergmann Date: Wed, 8 Dec 2021 14:39:19 +0100 Subject: [PATCH 06/11] WIP: make this work independent of the repo directory --- docker-compose.yml | 10 ++++++---- docker/database.Dockerfile | 3 +++ docker/start_server.sh | 13 +++++++++---- 3 files changed, 18 insertions(+), 8 deletions(-) create mode 100644 docker/database.Dockerfile diff --git a/docker-compose.yml b/docker-compose.yml index 519fcce3..eb7bb8a1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,11 +14,13 @@ services: - EXTERNAL_IP=${EXTERNAL_IP:-darkflame} volumes: - ${CLIENT_PATH:?err}:/client - - ./docker/:/docker/ + - shared_configs:/docker/ database: container_name: DarkflameDatabase - image: mariadb:10.6 + build: + context: . + dockerfile: ./docker/database.Dockerfile environment: - MARIADB_USER=${MARIADB_USER:-darkflame} - MARIADB_PASSWORD=${MARIADB_PASSWORD:-darkflame} @@ -26,7 +28,6 @@ services: - MARIADB_DATABASE=${MARIADB_DATABASE:-darkflame} volumes: - database:/var/lib/mysql - - ./migrations/dlu:/docker-entrypoint-initdb.d networks: - darkflame ports: @@ -44,7 +45,7 @@ services: - BUILD_VERSION=${BUILD_VERSION:-171022} volumes: - ${CLIENT_PATH:?err}:/client - - ./docker/configs/:/configs + - shared_configs:/shared_configs depends_on: - database ports: @@ -98,3 +99,4 @@ networks: volumes: database: + shared_configs: diff --git a/docker/database.Dockerfile b/docker/database.Dockerfile new file mode 100644 index 00000000..68261e44 --- /dev/null +++ b/docker/database.Dockerfile @@ -0,0 +1,3 @@ +FROM mariadb:10.6 + +COPY ./migrations/dlu /docker-entrypoint-initdb.d \ No newline at end of file diff --git a/docker/start_server.sh b/docker/start_server.sh index 9a2345c6..825d5014 100755 --- a/docker/start_server.sh +++ b/docker/start_server.sh @@ -21,10 +21,10 @@ function symlink_client_files() { function symlink_config_files() { echo "Creating symlinks for config files" rm /app/*.ini - ln -s /configs/authconfig.ini /app/authconfig.ini - ln -s /configs/chatconfig.ini /app/chatconfig.ini - ln -s /configs/masterconfig.ini /app/masterconfig.ini - ln -s /configs/worldconfig.ini /app/worldconfig.ini + ln -s /shared_configs/configs/authconfig.ini /app/authconfig.ini + ln -s /shared_configs/configs/chatconfig.ini /app/chatconfig.ini + ln -s /shared_configs/configs/masterconfig.ini /app/masterconfig.ini + ln -s /shared_configs/configs/worldconfig.ini /app/worldconfig.ini } if [[ ! -f "/app/initialized" ]]; then @@ -37,6 +37,11 @@ else echo "Server already initialized" fi +while [ ! -f "/client/migrated" ]; do + echo "Client setup not finished. Waiting for setup container to complete" + sleep 1 +done + # start the server echo "Start MasterServer" ./MasterServer From 7f1e392be271abb4c0fcc8ddd255c177aafd4d7f Mon Sep 17 00:00:00 2001 From: Nils Bergmann Date: Wed, 8 Dec 2021 14:57:16 +0100 Subject: [PATCH 07/11] WIP: add master_ip option --- dMasterServer/MasterServer.cpp | 10 ++++++++-- docker-compose.yml | 6 ++++-- docker/setup.sh | 3 +++ resources/masterconfig.ini | 3 +++ 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/dMasterServer/MasterServer.cpp b/dMasterServer/MasterServer.cpp index 926168a3..1c73fd22 100644 --- a/dMasterServer/MasterServer.cpp +++ b/dMasterServer/MasterServer.cpp @@ -157,10 +157,16 @@ int main(int argc, char** argv) { auto* masterLookupStatement = Database::CreatePreppedStmt("SELECT id FROM `servers` WHERE `name` = 'master'"); auto* result = masterLookupStatement->executeQuery(); + auto master_server_ip = config.GetValue("master_ip"); + + if (master_server_ip == "") { + master_server_ip = Game::server->GetIP(); + } + //If we found a server, update it's IP and port to the current one. if (result->next()) { auto* updateStatement = Database::CreatePreppedStmt("UPDATE `servers` SET `ip` = ?, `port` = ? WHERE `id` = ?"); - updateStatement->setString(1, Game::server->GetIP()); + updateStatement->setString(1, master_server_ip); updateStatement->setInt(2, Game::server->GetPort()); updateStatement->setInt(3, result->getInt("id")); updateStatement->execute(); @@ -169,7 +175,7 @@ int main(int argc, char** argv) { else { //If we didn't find a server, create one. auto* insertStatement = Database::CreatePreppedStmt("INSERT INTO `servers` (`name`, `ip`, `port`, `state`, `version`) VALUES ('master', ?, ?, 0, 171023)"); - insertStatement->setString(1, Game::server->GetIP()); + insertStatement->setString(1, master_server_ip); insertStatement->setInt(2, Game::server->GetPort()); insertStatement->execute(); delete insertStatement; diff --git a/docker-compose.yml b/docker-compose.yml index eb7bb8a1..7446748e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -30,8 +30,10 @@ services: - database:/var/lib/mysql networks: - darkflame - ports: - - 3306:3306 # (optional) ports only exposed so that DB management tools can connect + # (optional) ports only exposed so that DB management tools can connect + # Remove the # of the next two lines to expose your database + # ports: + # - 3306:3306 darkflame: container_name: DarkflameServer diff --git a/docker/setup.sh b/docker/setup.sh index 98a92515..14d0951f 100755 --- a/docker/setup.sh +++ b/docker/setup.sh @@ -31,6 +31,9 @@ function update_ini_values() { update_ini worldconfig.ini chat_server_port $CHAT_SERVER_PORT update_ini worldconfig.ini max_clients $MAX_CLIENTS + # always use the internal docker hostname + update_ini masterconfig.ini master_ip "darkflame" + update_database_ini_values_for masterconfig.ini update_database_ini_values_for authconfig.ini update_database_ini_values_for chatconfig.ini diff --git a/resources/masterconfig.ini b/resources/masterconfig.ini index 120a3743..531234b6 100644 --- a/resources/masterconfig.ini +++ b/resources/masterconfig.ini @@ -7,6 +7,9 @@ mysql_password= # The public facing IP address. Can be 'localhost' for locally hosted servers external_ip=localhost +# The internal ip of the master server +master_ip=localhost + # Port number port=2000 From 18d797150ea1390c31b8401e2e0d45fee8fb6571 Mon Sep 17 00:00:00 2001 From: Jack Kawell Date: Thu, 9 Dec 2021 00:10:29 -0700 Subject: [PATCH 08/11] PR comments --- .env.example | 4 ++-- docker-compose.yml | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.env.example b/.env.example index 22fff590..fdfe1c9b 100644 --- a/.env.example +++ b/.env.example @@ -9,7 +9,7 @@ ACCOUNT_MANAGER_SECRET= # Should be the externally facing IP of your server host EXTERNAL_IP=localhost # Database values -MARIADB_USER=SECRET_VALUE_CHANGE_ME +MARIADB_USER=darkflame MARIADB_PASSWORD=SECRET_VALUE_CHANGE_ME MARIADB_ROOT_PASSWORD=SECRET_VALUE_CHANGE_ME -MARIADB_DATABASE=SECRET_VALUE_CHANGE_ME \ No newline at end of file +MARIADB_DATABASE=darkflame \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 519fcce3..c71c8c8f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -29,8 +29,9 @@ services: - ./migrations/dlu:/docker-entrypoint-initdb.d networks: - darkflame - ports: - - 3306:3306 # (optional) ports only exposed so that DB management tools can connect + # You can expose these so that DB management tools can connect (WARNING: INSECURE) + # ports: + # - 3306:3306 darkflame: container_name: DarkflameServer From ecd717702d3cbd4fcae3963e84cbb9c990b2c454 Mon Sep 17 00:00:00 2001 From: Jack Kawell Date: Thu, 9 Dec 2021 00:20:10 -0700 Subject: [PATCH 09/11] Added client setup check to start_server.sh --- docker/setup.sh | 10 +++++----- docker/start_server.sh | 8 +++++++- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/docker/setup.sh b/docker/setup.sh index 98a92515..7bff747d 100755 --- a/docker/setup.sh +++ b/docker/setup.sh @@ -55,7 +55,7 @@ update_ini_values if [[ ! -d "/client" ]]; then echo "Client not found." - echo "Did you forgot to mount the client into the \"/client\" directory?" + echo "Did you forget to mount the client into the \"/client\" directory?" exit 1 fi @@ -66,8 +66,8 @@ if [[ ! -f "/client/extracted" ]]; then touch /client/extracted else - echo "Client already extracted. Skip this step" - echo "If you want to force re-extract, just delete the file called \"extracted\" in the client directory" + echo "Client already extracted. Skip this step..." + echo "If you want to force a re-extract, just delete the file called \"extracted\" in the client directory" fi if [[ ! -f "/client/migrated" ]]; then @@ -77,6 +77,6 @@ if [[ ! -f "/client/migrated" ]]; then touch /client/migrated else - echo "Client db already migrated. Skip this step" - echo "If you want to force re-migrate, just delete the file called \"migrated\" in the client directory" + echo "Client db already migrated. Skip this step..." + echo "If you want to force a re-migrate, just delete the file called \"migrated\" in the client directory" fi diff --git a/docker/start_server.sh b/docker/start_server.sh index 9a2345c6..6dccad7e 100755 --- a/docker/start_server.sh +++ b/docker/start_server.sh @@ -37,7 +37,13 @@ else echo "Server already initialized" fi +# check to make sure the setup has completed +while [ ! -f "/client/extracted" ] || [ ! -f "/client/migrated" ]; do + echo "Client setup not finished. Waiting for setup container to complete..." + sleep 5 +done + # start the server -echo "Start MasterServer" +echo "Starting MasterServer" ./MasterServer tail -f /dev/null \ No newline at end of file From 3b90669d0323fe7d147b50018e23cda7ba396f22 Mon Sep 17 00:00:00 2001 From: Jack Kawell Date: Thu, 9 Dec 2021 00:34:14 -0700 Subject: [PATCH 10/11] Added a link to keygen for flask secret --- .env.example | 1 + 1 file changed, 1 insertion(+) diff --git a/.env.example b/.env.example index fdfe1c9b..bbda7da6 100644 --- a/.env.example +++ b/.env.example @@ -5,6 +5,7 @@ BUILD_THREADS=1 # Updates NET_VERSION in CMakeVariables.txt BUILD_VERSION=171022 # make sure this is a long random string +# grab a "SHA 256-bit Key" from here: https://keygen.io/ ACCOUNT_MANAGER_SECRET= # Should be the externally facing IP of your server host EXTERNAL_IP=localhost From 56f860676d78051ac35f3884454a1c8c829f1d07 Mon Sep 17 00:00:00 2001 From: Jack Kawell Date: Thu, 9 Dec 2021 00:37:14 -0700 Subject: [PATCH 11/11] Added admin setup command to docs --- Docker.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Docker.md b/Docker.md index 0e6ae3d7..8bc12ada 100644 --- a/Docker.md +++ b/Docker.md @@ -14,8 +14,11 @@ 4. Run `docker-compose up --build setup` 5. Run `docker-compose up -d database` 6. Run `docker-compose up --build -d account-manager brickbuildfix` -7. Run `docker-compose up --build -d darkflame` -8. Now you can see the output of the server with `docker compose logs -f --tail 100` or `docker-compose logs -f --tail 100`. This can help you understand issues and there you can also see when the server finishes it's startup. +7. Run `docker-compose build darkflame` +8. Run `docker-compose exec darkflame /app/MasterServer -a` and setup your admin account +9. Run `docker-compose up -d darkflame` +10. Now you can see the output of the server with `docker compose logs -f --tail 100` or `docker-compose logs -f --tail 100`. This can help you understand issues and there you can also see when the server finishes it's startup. +11. You're ready to connect your client! ## Disable brickbuildfix