Merge pull request #3 from jgkawell/pr-improvements

Dockerized improvements
This commit is contained in:
Nils Bergmann 2021-12-09 18:46:44 +01:00 committed by GitHub
commit ef7dd0f069
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 280 additions and 255 deletions

View File

@ -5,4 +5,12 @@ BUILD_THREADS=1
# Updates NET_VERSION in CMakeVariables.txt # Updates NET_VERSION in CMakeVariables.txt
BUILD_VERSION=171022 BUILD_VERSION=171022
# make sure this is a long random string # make sure this is a long random string
# grab a "SHA 256-bit Key" from here: https://keygen.io/
ACCOUNT_MANAGER_SECRET= ACCOUNT_MANAGER_SECRET=
# Should be the externally facing IP of your server host
EXTERNAL_IP=localhost
# Database values
MARIADB_USER=darkflame
MARIADB_PASSWORD=SECRET_VALUE_CHANGE_ME
MARIADB_ROOT_PASSWORD=SECRET_VALUE_CHANGE_ME
MARIADB_DATABASE=darkflame

1
.gitignore vendored
View File

@ -1,6 +1,7 @@
temp/ temp/
cmake-build-debug/ cmake-build-debug/
RelWithDebInfo/ RelWithDebInfo/
docker/configs
# Third party libraries # Third party libraries
thirdparty/mysql/ thirdparty/mysql/

View File

@ -95,7 +95,7 @@ if (__include_backtrace__ AND __compile_backtrace__)
link_directories(${backtrace_SOURCE_DIR}/.libs/) link_directories(${backtrace_SOURCE_DIR}/.libs/)
include_directories(${backtrace_SOURCE_DIR}) include_directories(${backtrace_SOURCE_DIR})
endif(__include_backtrace__ AND __compile_backtrace__) endif(__include_backtrace__)
endif() endif()
endif(UNIX) endif(UNIX)

View File

@ -1,22 +1,26 @@
# Run the Darkflame Server inside Docker # Run the Darkflame Server inside Docker
### What you need ## What you need
- Docker (Docker Desktop or on Linux normal Docker) - Docker (Docker Desktop or on Linux normal Docker)
- Docker-Compose (Included in Docker Desktop) - Docker-Compose (Included in Docker Desktop)
- LEGO® Universe Client - 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 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=` 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 3. Update other values in the `.env` file as need (be sure to update passwords!)
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. 4. Run `docker-compose up --build setup`
5. Run `docker compose up -d --build` or `docker-compose up -d --build` and wait for it to complete 5. Run `docker-compose up -d database`
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. 6. Run `docker-compose up --build -d account-manager brickbuildfix`
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
### Disable brickbuildfix
If you don't need the http server running on port 80 do this: If you don't need the http server running on port 80 do this:
@ -30,4 +34,4 @@ services:
- donotstart - donotstart
``` ```
4. Now run `docker compose up -d --build` or `docker-compose up -d --build` 3. Now run `docker compose up -d --build` or `docker-compose up -d --build`

View File

@ -1222,19 +1222,13 @@ void Entity::UpdateXMLDoc(tinyxml2::XMLDocument* doc) {
} }
void Entity::Update(const float deltaTime) { void Entity::Update(const float deltaTime) {
int timerSize = m_Timers.size(); for (int i = 0; i < m_Timers.size(); i++) {
for (int i = 0; i < timerSize; i++) {
m_Timers[i]->Update(deltaTime); m_Timers[i]->Update(deltaTime);
if (m_Timers[i]->GetTime() <= 0) { if (m_Timers[i]->GetTime() <= 0) {
const auto timerName = m_Timers[i]->GetName(); 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]; delete m_Timers[i];
m_Timers.erase(m_Timers.begin() + 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
for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) { for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) {
script->OnTimerDone(this, timerName); script->OnTimerDone(this, timerName);

View File

@ -157,10 +157,16 @@ int main(int argc, char** argv) {
auto* masterLookupStatement = Database::CreatePreppedStmt("SELECT id FROM `servers` WHERE `name` = 'master'"); auto* masterLookupStatement = Database::CreatePreppedStmt("SELECT id FROM `servers` WHERE `name` = 'master'");
auto* result = masterLookupStatement->executeQuery(); 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 we found a server, update it's IP and port to the current one.
if (result->next()) { if (result->next()) {
auto* updateStatement = Database::CreatePreppedStmt("UPDATE `servers` SET `ip` = ?, `port` = ? WHERE `id` = ?"); 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(2, Game::server->GetPort());
updateStatement->setInt(3, result->getInt("id")); updateStatement->setInt(3, result->getInt("id"));
updateStatement->execute(); updateStatement->execute();
@ -169,7 +175,7 @@ int main(int argc, char** argv) {
else { else {
//If we didn't find a server, create one. //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)"); 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->setInt(2, Game::server->GetPort());
insertStatement->execute(); insertStatement->execute();
delete insertStatement; delete insertStatement;

View File

@ -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<LWOOBJID>(u"ShakeObject"));
const auto randomTime = self->GetVar<int>(u"RandomTime");
auto time = GeneralUtils::GenerateRandomNumber<int>(0, randomTime + 1);
if (time < randomTime / 2) {
time += randomTime / 2;
}
self->AddTimer("ShipShakeIdle", static_cast<float>(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<int>(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;
}

View File

@ -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);
};

View File

@ -1,6 +1,7 @@
#include "AgSpaceStuff.h" #include "AgSpaceStuff.h"
#include "GeneralUtils.h" #include "GeneralUtils.h"
#include "GameMessages.h" #include "GameMessages.h"
#include "dZoneManager.h"
#include "EntityManager.h" #include "EntityManager.h"
#include "Game.h" #include "Game.h"
@ -16,6 +17,11 @@ void AgSpaceStuff::OnStartup(Entity* self) {
auto* ref = EntityManager::Instance()->CreateEntity(info); auto* ref = EntityManager::Instance()->CreateEntity(info);
EntityManager::Instance()->ConstructEntity(ref); 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) { 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))); GameMessages::SendPlayAnimation(self, u"path_0" + (GeneralUtils::to_u16string(pathType)));
self->AddTimer("FloaterScale", randTime); 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<LWOOBJID>(u"ShakeObject"));
const auto randomTime = self->GetVar<int>(u"RandomTime");
auto time = GeneralUtils::GenerateRandomNumber<int>(0, randomTime + 1);
if (time < randomTime / 2) {
time += randomTime / 2;
}
self->AddTimer("ShipShakeIdle", static_cast<float>(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<int>(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;
} }

View File

@ -5,4 +5,14 @@ class AgSpaceStuff : public CppScripts::Script {
public: public:
void OnStartup(Entity* self); void OnStartup(Entity* self);
void OnTimerDone(Entity* self, std::string timerName); 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);
}; };

View File

@ -15,7 +15,6 @@
//VE / AG scripts: //VE / AG scripts:
#include "AgShipPlayerDeathTrigger.h" #include "AgShipPlayerDeathTrigger.h"
#include "AgShipPlayerShockServer.h" #include "AgShipPlayerShockServer.h"
#include "AgShipShake.h"
#include "AgSpaceStuff.h" #include "AgSpaceStuff.h"
#include "AgImagSmashable.h" #include "AgImagSmashable.h"
#include "NpcNpSpacemanBob.h" #include "NpcNpSpacemanBob.h"
@ -291,9 +290,7 @@ CppScripts::Script* CppScripts::GetScript(Entity* parent, const std::string& scr
script = new AgShipPlayerDeathTrigger(); script = new AgShipPlayerDeathTrigger();
else if (scriptName == "scripts\\ai\\NP\\L_NPC_NP_SPACEMAN_BOB.lua") else if (scriptName == "scripts\\ai\\NP\\L_NPC_NP_SPACEMAN_BOB.lua")
script = new NpcNpSpacemanBob(); script = new NpcNpSpacemanBob();
else if (scriptName == "scripts\\ai\\AG\\L_AG_SHIP_SHAKE.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 AgShipShake();
else if (scriptName == "scripts\\ai\\AG\\L_AG_SPACE_STUFF.lua")
script = new AgSpaceStuff(); script = new AgSpaceStuff();
else if (scriptName == "scripts\\ai\\AG\\L_AG_SHIP_PLAYER_SHOCK_SERVER.lua") else if (scriptName == "scripts\\ai\\AG\\L_AG_SHIP_PLAYER_SHOCK_SERVER.lua")
script = new AgShipPlayerShockServer(); script = new AgShipPlayerShockServer();

View File

@ -1,9 +1,26 @@
version: "3" version: "3"
services: 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
- shared_configs:/docker/
database: database:
container_name: DarkflameDatabase container_name: DarkflameDatabase
image: mariadb:10.6 build:
context: .
dockerfile: ./docker/database.Dockerfile
environment: environment:
- MARIADB_USER=${MARIADB_USER:-darkflame} - MARIADB_USER=${MARIADB_USER:-darkflame}
- MARIADB_PASSWORD=${MARIADB_PASSWORD:-darkflame} - MARIADB_PASSWORD=${MARIADB_PASSWORD:-darkflame}
@ -11,12 +28,14 @@ services:
- MARIADB_DATABASE=${MARIADB_DATABASE:-darkflame} - MARIADB_DATABASE=${MARIADB_DATABASE:-darkflame}
volumes: volumes:
- database:/var/lib/mysql - database:/var/lib/mysql
- ./migrations/dlu:/docker-entrypoint-initdb.d
networks: networks:
- darkflame - darkflame
# You can expose these so that DB management tools can connect (WARNING: INSECURE)
# ports:
# - 3306:3306
darkflame: darkflame:
container_name: DarkFlameServer container_name: DarkflameServer
networks: networks:
- darkflame - darkflame
build: build:
@ -25,13 +44,9 @@ services:
args: args:
- BUILD_THREADS=${BUILD_THREADS:-1} - BUILD_THREADS=${BUILD_THREADS:-1}
- BUILD_VERSION=${BUILD_VERSION:-171022} - BUILD_VERSION=${BUILD_VERSION:-171022}
environment:
- DATABASE=${MARIADB_DATABASE:-darkflame}
- DATABASE_HOST=database
- DATABASE_USER=${MARIADB_USER:-darkflame}
- DATABASE_PASSWORD=${MARIADB_PASSWORD:-darkflame}
volumes: volumes:
- ${CLIENT_PATH:?err}:/client - ${CLIENT_PATH:?err}:/client
- shared_configs:/shared_configs
depends_on: depends_on:
- database - database
ports: ports:
@ -41,7 +56,7 @@ services:
- "3000-3300:3000-3300/udp" - "3000-3300:3000-3300/udp"
brickbuildfix: brickbuildfix:
container_name: DarkFlameBrickBuildFix container_name: DarkflameBrickBuildFix
networks: networks:
- darkflame - darkflame
build: build:
@ -57,7 +72,7 @@ services:
start_period: 40s start_period: 40s
account-manager: account-manager:
container_name: DarkFlameAccountManager container_name: DarkflameAccountManager
networks: networks:
- darkflame - darkflame
build: build:
@ -85,3 +100,4 @@ networks:
volumes: volumes:
database: database:
shared_configs:

View File

@ -43,7 +43,7 @@ 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 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 && \ apt remove -y libmysqlcppconn7v5 libmysqlcppconn-dev && \
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*
@ -51,14 +51,9 @@ WORKDIR /app
COPY --from=build /build/build /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 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 COPY docker/start_server.sh /start_server.sh
RUN chmod +x /start_server.sh
CMD [ "/start_server.sh" ] CMD [ "/start_server.sh" ]

View File

@ -0,0 +1,3 @@
FROM mariadb:10.6
COPY ./migrations/dlu /docker-entrypoint-initdb.d

14
docker/setup.Dockerfile Normal file
View File

@ -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" ]

85
docker/setup.sh Executable file
View File

@ -0,0 +1,85 @@
#!/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
# 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
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 forget 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 a 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 a re-migrate, just delete the file called \"migrated\" in the client directory"
fi

122
docker/start_server.sh Normal file → Executable file
View File

@ -1,71 +1,14 @@
#!/bin/bash #!/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}
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"
}
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 "darkflame"
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() { function symlink_client_files() {
echo "Creating symlinks for client files"
ln -s /client/client/res/macros/ /app/res/macros ln -s /client/client/res/macros/ /app/res/macros
ln -s /client/client/res/BrickModels/ /app/res/BrickModels 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/chatplus_en_us.txt /app/res/chatplus_en_us.txt
ln -s /client/client/res/names/ /app/res/names 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 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 cd /client/client/res/maps
readarray -d '' entries < <(printf '%s\0' * | sort -zV) readarray -d '' entries < <(printf '%s\0' * | sort -zV)
@ -75,49 +18,32 @@ function symlink_client_files() {
) )
} }
function fdb_to_sqlite() { function symlink_config_files() {
echo "Run fdb_to_sqlite" echo "Creating symlinks for config files"
python3 /app/utils/fdb_to_sqlite.py /client/client/res/CDClient.fdb --sqlite_path /app/res/CDServer.sqlite rm /app/*.ini
ln -s /shared_configs/configs/authconfig.ini /app/authconfig.ini
( ln -s /shared_configs/configs/chatconfig.ini /app/chatconfig.ini
cd /app/migrations/cdserver ln -s /shared_configs/configs/masterconfig.ini /app/masterconfig.ini
readarray -d '' entries < <(printf '%s\0' *.sql | sort -zV) ln -s /shared_configs/configs/worldconfig.ini /app/worldconfig.ini
for entry in "${entries[@]}"; do
echo "Execute $entry"
sqlite3 /app/res/CDServer.sqlite < $entry
done
)
} }
set_defaults if [[ ! -f "/app/initialized" ]]; then
# setup symlinks for volume files
check_sql_connection symlink_client_files
symlink_config_files
update_ini_values # do not run symlinks more than once
touch /app/initialized
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 else
echo "Client already extracted. Skip this step" echo "Server already initialized"
echo "If you want to force re-extract, just delete the file called \"extracted\" in the client directory"
fi fi
symlink_client_files # check to make sure the setup has completed
while [ ! -f "/client/extracted" ] || [ ! -f "/client/migrated" ]; do
fdb_to_sqlite echo "Client setup not finished. Waiting for setup container to complete..."
sleep 5
echo "Start MasterServer" done
# start the server
echo "Starting MasterServer"
./MasterServer ./MasterServer
tail -f /dev/null tail -f /dev/null

View File

@ -7,6 +7,9 @@ mysql_password=
# The public facing IP address. Can be 'localhost' for locally hosted servers # The public facing IP address. Can be 'localhost' for locally hosted servers
external_ip=localhost external_ip=localhost
# The internal ip of the master server
master_ip=localhost
# Port number # Port number
port=2000 port=2000