diff --git a/.env.example b/.env.example new file mode 100644 index 000000000..39f40a884 --- /dev/null +++ b/.env.example @@ -0,0 +1,11 @@ +# ENVIRONMENT determines the folder where we'll look for binaries. +# If you're using Release, change the build command to: docker compose run --rm melia-server dotnet publish +# Release performance tends to have better performance, Debug will have more info for development. + +ENVIRONMENT="Debug" # Options: Release | Debug + +# Database password +MYSQL_ROOT_PASSWORD="123456" # Use a strong password, change in ./user/conf/database.conf too + +# Automatically restart server. If using on-failure, sets amount of retries +SERVER_RESTART="unless-stopped" # Options: no | unless-stopped | always | on-failure:5 \ No newline at end of file diff --git a/.gitignore b/.gitignore index d75bfd214..df9864c6b 100644 --- a/.gitignore +++ b/.gitignore @@ -5,10 +5,12 @@ /log/ /logs/ /cache/ - +/.mysql/ /user/** +/.env !/user/conf/database-example.conf +!/user/conf/database-example-for-docker.conf !/user/db/items-example.txt !/user/scripts/scripts_custom-example.txt !/user/scripts/scripts_content-example.txt diff --git a/README.md b/README.md index 23de2e0fd..2964038d2 100644 --- a/README.md +++ b/README.md @@ -12,13 +12,6 @@ with any services provided by game developers or publishers, and we don't endorse such actions. We're here to learn and create, not to steal or destroy. -Client ------------------------------------------------------------------------------ - -Melia does not have a client of its own at this time. Instead, it's designed -to be network compatible with the latest client of the international -version of ToS, which is freely available on Steam. - State of Development ----------------------------------------------------------------------------- @@ -29,6 +22,7 @@ of the typical features you would expect from an online RPG, but there's still a way to go before we'd call it truly playable. Specifically, some of the major features that are working are as follows: + - Characters (creation, deletion, etc.) - Inventory (managing items, equipping, etc.) - Chat @@ -41,7 +35,116 @@ Specifically, some of the major features that are working are as follows: - Monster spawns - Quests -Requirements +Installation with Docker +----------------------------------------------------------------------------- + +This branch aims to make Melia easier to setup and run in any environment. +To achieve this, we're making a few changes do add the capability of building +and running Melia on Linux, as well as building and running the server in a +few Docker containers. + +For this to work, you will need [Docker](https://docs.docker.com/) installed +on your system. Recent versions of Docker comes with [Docker Compose](https://docs.docker.com/compose/) +pre-installed, so you don't have to worry about it. + +After cloning or downloading this repository, rename file `.env.example` to +`.env` and change configuration as needed. Then, navigate to the project folder +(where you'll find `docker-compose.yml` ) and run: + +```bash +docker compose build +``` + +This will download the necessary Docker Images for our project to run and +build a new image with `./Servers/Dockerfile`, where our server dependencies +are listed. + +Now, you can run: + +```bash +docker compose run --rm melia-server dotnet build +``` + +This will create our server binaries, making it ready to start with our next +command: + +```bash +docker compose up -d +``` + +Wait for a few seconds for everything to initialize. Then, you can go to your +browser and access [http://127.0.0.1/toslive/patch/serverlist.xml] to check +if server is up and running. + +You can restart server if anything goes wrong using: + +```bash +docker compose restart +``` + +If you change anything on source code, you need to run +`docker compose run --rm melia-server dotnet build` again and restart your +server with `docker compose restart` + +Database with Docker +----------------------------------------------------------------------------- + +With the Docker containers, we've added a PHPMyAdmin container, which can be +accessed via [http://127.0.0.1:8080]. Default user is `root` and password +is `123456` (You can change password on `.env`, but remember to change on +`user/conf/database.conf` as well). + +Client +----------------------------------------------------------------------------- + +Melia does not have a client of its own at this time. Instead, it's designed +to be network compatible with the latest client of the international +version of ToS, which is freely available on Steam. + +After downloading, open your client folder (Steam > Right-Click on Game > +Manage > Browse Local Files). Then, open `release/client.xml` with a text +editor. + +Change the line which starts with ` +``` + +Note: If your server is not on the same machine you're playing, change +`127.0.0.1` to your Server's LAN / WAN IP. + +Now you can open your game and play. + +If you're using Docker, you can't use server's console to create your +account, so, when logging in for the first time, use `new__` as a prefix +to your username. + +Example: + +```bash +Username: new__myaccount +Password: mypassword +``` + +This will create an account with username `myaccount` and password +`mypassword`. You can check the database to make sure everything is okay, +but you'll already be connected and playing. Next time, just login with +your username without the `new__` prefix. + +Installation from scratch +----------------------------------------------------------------------------- + +- Compile Melia +- Run `sql/main.sql` to setup the database +- Copy `system/conf/database.conf` to `user/conf/`, + adjust the necessary values and remove the rest. + +Afterwards, you should be able to start Melia via the provided scripts or +directly from the bin directories. If not, or if you need a more detailed +guide, head over to our forum, the chat, or the wiki. + +Requirements to build from scratch ----------------------------------------------------------------------------- Melia is being developed in C# (.NET 8+) and uses a MySQL database for @@ -57,18 +160,6 @@ macOS, you will need to install the SDK as well. For more detailed instructions, please wait patiently while we give our documentation a much-needed overhaul. It's only a matter of time. -Installation ------------------------------------------------------------------------------ - -* Compile Melia -* Run `sql/main.sql` to setup the database -* Copy `system/conf/database.conf` to `user/conf/`, - adjust the necessary values and remove the rest. - -Afterwards, you should be able to start Melia via the provided scripts or -directly from the bin directories. If not, or if you need a more detailed -guide, head over to our forum, the chat, or the wiki. - Contribution ----------------------------------------------------------------------------- @@ -77,8 +168,7 @@ Check the file CONTRIBUTING.md for instructions on how you may contribute. Links ----------------------------------------------------------------------------- -* GitHub: https://github.com/NoCode-NoLife/melia -* Wiki: https://github.com/NoCode-NoLife/melia/wiki -* Forum: https://nocodenolife.org/forum/65-melia/ -* Chat: https://discord.gg/5sszEgw - +- GitHub: https://github.com/NoCode-NoLife/melia +- Wiki: https://github.com/NoCode-NoLife/melia/wiki +- Forum: https://nocodenolife.org/forum/65-melia/ +- Chat: https://discord.gg/5sszEgw diff --git a/Servers/Dockerfile b/Servers/Dockerfile new file mode 100644 index 000000000..67fdb3b52 --- /dev/null +++ b/Servers/Dockerfile @@ -0,0 +1,10 @@ +FROM ubuntu:22.04 +ARG DEBIAN_FRONTEND=noninteractive + +WORKDIR / + +RUN apt update -y && apt upgrade -y && apt install -y dotnet-sdk-8.0 aspnetcore-runtime-8.0 git php libapache2-mod-php php-cgi php-mysql vim + +WORKDIR /melia + +CMD ["./start", "Debug", "BarracksServer"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..911b399b6 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,65 @@ +name: "melia" +services: + melia-database: + container_name: melia-database + image: mysql:8.3 + networks: + - melia-network + ports: + - 3306:3306 + environment: + - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} + volumes: + - ./sql:/docker-entrypoint-initdb.d + - ./.mysql/:/var/lib/mysql + restart: ${SERVER_RESTART} + env_file: + - ./.env + healthcheck: + test: "/usr/bin/mysql --user=root --password=${MYSQL_ROOT_PASSWORD} --execute \"SHOW DATABASES;\"" + interval: 5s + timeout: 10s + retries: 40 + + melia-server: + container_name: melia-server + command: "./start ${ENVIRONMENT}" + # command: "tail -f /dev/null" + build: "./Servers" + networks: + - melia-network + volumes: + - ./:/melia + depends_on: + melia-database: + condition: service_healthy + ports: + - "2000:2000" + - "6001:6001" + - "7001:7001" + - "7002:7002" + - "80:80" + restart: ${SERVER_RESTART} + env_file: + - ./.env + phpmyadmin: + container_name: melia-dbadmin + networks: + - melia-network + image: phpmyadmin + ports: + - 8080:80 + restart: ${SERVER_RESTART} + environment: + - PMA_HOST=melia-database + - PMA_USER=root + - PMA_PASSWORD=${MYSQL_ROOT_PASSWORD} + - UPLOAD_LIMIT=300M + profiles: [] + env_file: + - ./.env + depends_on: + melia-database: + condition: service_healthy +networks: + melia-network: diff --git a/sql/updates/update_2024-08-11_1.sql b/sql/updates/update_2024-08-11_1.sql index 7ce4ba50d..e83a8e58d 100644 --- a/sql/updates/update_2024-08-11_1.sql +++ b/sql/updates/update_2024-08-11_1.sql @@ -1,18 +1,17 @@ -CREATE TABLE `character_etc_properties` ( - `propertyId` bigint(20) NOT NULL, +CREATE TABLE IF NOT EXISTS `character_etc_properties` ( + `propertyId` bigint(20) NOT NULL AUTO_INCREMENT, `characterId` bigint(20) NOT NULL, `name` varchar(64) NOT NULL, `type` varchar(1) NOT NULL, - `value` varchar(255) NOT NULL + `value` varchar(255) NOT NULL, + PRIMARY KEY (`propertyId`), + KEY `characterId` (`characterId`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci; -ALTER TABLE `character_etc_properties` - ADD PRIMARY KEY (`propertyId`), - ADD KEY `characterId` (`characterId`); - -ALTER TABLE `character_etc_properties` - MODIFY `propertyId` bigint(20) NOT NULL AUTO_INCREMENT; +-- Drop the foreign key if it exists +-- ALTER TABLE `character_etc_properties` +-- DROP FOREIGN KEY `character_etc_properties_ibfk_1`; +-- Add the foreign key constraint again ALTER TABLE `character_etc_properties` - ADD CONSTRAINT `character_etc_properties_ibfk_1` FOREIGN KEY (`characterId`) REFERENCES `characters` (`characterId`) ON DELETE CASCADE ON UPDATE CASCADE, - ADD CONSTRAINT `character_etc_properties_ibfk_2` FOREIGN KEY (`characterId`) REFERENCES `characters` (`characterId`) ON DELETE CASCADE ON UPDATE CASCADE; +ADD CONSTRAINT `character_etc_properties_ibfk_1` FOREIGN KEY (`characterId`) REFERENCES `characters` (`characterId`) ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/sql/updates/update_2024-08-11_2.sql b/sql/updates/update_2024-08-11_2.sql index 69fee9512..948950c7f 100644 --- a/sql/updates/update_2024-08-11_2.sql +++ b/sql/updates/update_2024-08-11_2.sql @@ -1,13 +1,23 @@ -CREATE TABLE `storage_team` ( +CREATE TABLE IF NOT EXISTS `storage_team` ( `accountId` bigint(20) NOT NULL, `itemId` bigint(20) NOT NULL, - `position` int(11) NOT NULL + `position` int(11) NOT NULL, + PRIMARY KEY (`accountId`, `itemId`), + KEY `itemId` (`itemId`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci; +-- Drop the first foreign key if it exists +-- ALTER TABLE `storage_team` +-- DROP FOREIGN KEY `storage_team_ibfk_1`; + +-- Drop the second foreign key if it exists +-- ALTER TABLE `storage_team` +-- DROP FOREIGN KEY `storage_team_ibfk_2`; + +-- Add the first foreign key constraint ALTER TABLE `storage_team` - ADD PRIMARY KEY (`accountId`,`itemId`), - ADD KEY `itemId` (`itemId`); +ADD CONSTRAINT `storage_team_ibfk_1` FOREIGN KEY (`accountId`) REFERENCES `accounts` (`accountId`) ON DELETE CASCADE ON UPDATE CASCADE; +-- Add the second foreign key constraint ALTER TABLE `storage_team` - ADD CONSTRAINT `storage_team_ibfk_1` FOREIGN KEY (`accountId`) REFERENCES `accounts` (`accountId`) ON DELETE CASCADE ON UPDATE CASCADE, - ADD CONSTRAINT `storage_team_ibfk_2` FOREIGN KEY (`itemId`) REFERENCES `items` (`itemUniqueId`) ON DELETE CASCADE ON UPDATE CASCADE; +ADD CONSTRAINT `storage_team_ibfk_2` FOREIGN KEY (`itemId`) REFERENCES `items` (`itemUniqueId`) ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/src/BarracksServer/BarracksServer.cs b/src/BarracksServer/BarracksServer.cs index 6af6bbb60..601f57862 100644 --- a/src/BarracksServer/BarracksServer.cs +++ b/src/BarracksServer/BarracksServer.cs @@ -231,9 +231,9 @@ private void Communicator_OnRequestReceived(string sender, RequestMessage reques var message = new ResServerListMessage(servers); var responseMessage = new ResponseMessage(requestMessage.Id, message); - this.Communicator.Send(sender, responseMessage); - break; - } + this.Communicator.Send(sender, responseMessage); + break; + } } } diff --git a/src/Shared/Configuration/Files/Web.cs b/src/Shared/Configuration/Files/Web.cs index a457c18f8..f30ea2620 100644 --- a/src/Shared/Configuration/Files/Web.cs +++ b/src/Shared/Configuration/Files/Web.cs @@ -1,4 +1,5 @@ -using System.IO; +using System; +using System.IO; using Yggdrasil.Configuration; namespace Melia.Shared.Configuration.Files @@ -17,8 +18,12 @@ public class WebConfFile : ConfFile public void Load(string filePath) { this.Include(filePath); - - this.PhpCgiFilePath = this.GetString("php_cgi_bin", Path.Combine("user", "tools", "php", "php-cgi.exe")); + if (Environment.OSVersion.Platform == PlatformID.Win32NT) + { + this.PhpCgiFilePath = this.GetString("php_cgi_bin", Path.Combine("user", "tools", "php", "php-cgi.exe")); + } else { + this.PhpCgiFilePath = this.GetString("php_cgi_bin", "/usr/bin/php-cgi"); + } } } } diff --git a/src/ZoneServer/Commands/ChatCommands.Handlers.cs b/src/ZoneServer/Commands/ChatCommands.Handlers.cs index 22fadaaa2..49667bc16 100644 --- a/src/ZoneServer/Commands/ChatCommands.Handlers.cs +++ b/src/ZoneServer/Commands/ChatCommands.Handlers.cs @@ -65,7 +65,21 @@ public ChatCommands() this.Add("item", " [amount]", "Spawns item.", this.HandleItem); this.Add("silver", "", "Spawns silver.", this.HandleSilver); this.Add("spawn", " [amount=1] ['ai'=BasicMonster] ['tendency'=peaceful] ['hp'=amount]", "Spawns monster.", this.HandleSpawn); - this.Add("madhatter", "", "Spawns all headgears.", this.HandleGetAllHats); + + this.Add("madhatter", "", "Spawns all hair accessories.", this.HandleGetAllHairAccessories); + this.Add("getallwings", "", "Spawns all wings.", this.HandleGetAllWings); + this.Add("getalltoys", "", "Spawns all toys.", this.HandleGetAllToys); + this.Add("getallarmbands", "", "Spawns all armbands.", this.HandleGetAllArmbands); + this.Add("getallcostumes", "", "Spawns all costumes.", this.HandleGetAllCostumes); + this.Add("getalllens", "", "Spawns all lens.", this.HandleGetAllLens); + this.Add("getalleffectcostumes", "", "Spawns all effect costumes.", this.HandleGetAllEffectCostumes); + this.Add("getallspecialcostumes", "", "Spawns all special costumes.", this.HandleGetAllSpecialCostumes); + this.Add("getallspecialcostumeskins", "", "Spawns all special costume skins.", this.HandleGetAllSpecialCostumeSkins); + this.Add("getallhairs", "", "Spawns all hairs.", this.HandleGetAllHairs); + this.Add("getallhelmets", "", "Spawns all helmets.", this.HandleGetAllHelmets); + this.Add("getalldolls", "", "Spawns all dolls.", this.HandleGetAllDolls); + this.Add("removeallfromgender", "", "Remove All from gender.", this.HandleRemoveAllFromGender); + this.Add("levelup", "", "Increases character's level.", this.HandleLevelUp); this.Add("joblevelup", "", "Increases character's job level.", this.HandleJobLevelUp); this.Add("speed", "", "Modifies character's speed.", this.HandleSpeed); @@ -684,10 +698,284 @@ private CommandResult HandleSpawn(Character sender, Character target, string mes /// /// /// - private CommandResult HandleGetAllHats(Character sender, Character target, string message, string command, Arguments args) + private CommandResult HandleGetAllHairAccessories(Character sender, Character target, string message, string command, Arguments args) + { + int startId=628001; + int endId=629511; + int[] additionalItems = new int[] { }; + string itemTypeName = "hair accessories"; + return HandleGetAllSelectedItems(sender, target, message, command, args, startId, endId, additionalItems, itemTypeName); + } + + /// + /// Adds all available wings to target's inventory. + /// + /// + /// + /// + /// + /// + /// + private CommandResult HandleGetAllWings(Character sender, Character target, string message, string command, Arguments args) + { + int startId=637000; + int endId=637999; + int[] additionalItems = new int[] { 635572 }; // Add the Little Ghost Balloon which is off ID range + string itemTypeName = "wings"; + return HandleGetAllSelectedItems(sender, target, message, command, args, startId, endId, additionalItems, itemTypeName); + } + + /// + /// Adds all available toys to target's inventory. + /// + /// + /// + /// + /// + /// + /// + private CommandResult HandleGetAllToys(Character sender, Character target, string message, string command, Arguments args) + { + int startId=630001; + int endId=635121; + int[] additionalItems = new int[] { }; + string itemTypeName = "toys"; + return HandleGetAllSelectedItems(sender, target, message, command, args, startId, endId, additionalItems, itemTypeName); + } + + /// + /// Adds all available armbands to target's inventory. + /// + /// + /// + /// + /// + /// + /// + private CommandResult HandleGetAllArmbands(Character sender, Character target, string message, string command, Arguments args) + { + int startId=11101; + int endId=11147; + int[] additionalItems = new int[] { }; + string itemTypeName = "armbands"; + return HandleGetAllSelectedItems(sender, target, message, command, args, startId, endId, additionalItems, itemTypeName); + } + + /// + /// Adds all available costumes to target's inventory. + /// + /// + /// + /// + /// + /// + /// + private CommandResult HandleGetAllCostumes(Character sender, Character target, string message, string command, Arguments args) + { + int startId=620101; + int endId=635510; + int[] additionalItems = new int[] { 635570, 635571 }; // Reissue Cafe T-shirt Male and Female + string itemTypeName = "costumes"; + return HandleGetAllSelectedItems(sender, target, message, command, args, startId, endId, additionalItems, itemTypeName); + } + + /// + /// Adds all available lens to target's inventory. + /// + /// + /// + /// + /// + /// + /// + private CommandResult HandleGetAllLens(Character sender, Character target, string message, string command, Arguments args) + { + int startId=18001; + int endId=18016; + int[] additionalItems = new int[] { }; + string itemTypeName = "lens"; + return HandleGetAllSelectedItems(sender, target, message, command, args, startId, endId, additionalItems, itemTypeName); + } + + /// + /// Adds all available effect costumes to target's inventory. + /// + /// + /// + /// + /// + /// + /// + private CommandResult HandleGetAllEffectCostumes(Character sender, Character target, string message, string command, Arguments args) + { + int startId=639101; + int endId=639126; + int[] additionalItems = new int[] { 640000 }; // White Snowflake Crystal + string itemTypeName = "effect costumes"; + return HandleGetAllSelectedItems(sender, target, message, command, args, startId, endId, additionalItems, itemTypeName); + } + + /// + /// Adds all available special costumes to target's inventory. + /// + /// + /// + /// + /// + /// + /// + private CommandResult HandleGetAllSpecialCostumes(Character sender, Character target, string message, string command, Arguments args) + { + int startId=638000; + int endId=638012; + int[] additionalItems = new int[] { }; + string itemTypeName = "special costumes"; + return HandleGetAllSelectedItems(sender, target, message, command, args, startId, endId, additionalItems, itemTypeName); + } + + /// + /// Adds all available special costume skins to target's inventory. + /// + /// + /// + /// + /// + /// + /// + private CommandResult HandleGetAllSpecialCostumeSkins(Character sender, Character target, string message, string command, Arguments args) + { + int startId=750000; + int endId=750008; + int[] additionalItems = new int[] { }; + string itemTypeName = "special costume skins"; + return HandleGetAllSelectedItems(sender, target, message, command, args, startId, endId, additionalItems, itemTypeName); + } + + /// + /// Adds all available hairs to target's inventory. + /// + /// + /// + /// + /// + /// + /// + private CommandResult HandleGetAllHairs(Character sender, Character target, string message, string command, Arguments args) + { + int startId=12111; + int endId=19003; + int[] additionalItems = new int[] { 635054, 635055 }; // [EVENT] Cockatrice Head (Male, Female) + string itemTypeName = "hairs"; + return HandleGetAllSelectedItems(sender, target, message, command, args, startId, endId, additionalItems, itemTypeName); + } + + /// + /// Adds all available helmets to target's inventory. + /// + /// + /// + /// + /// + /// + /// + private CommandResult HandleGetAllHelmets(Character sender, Character target, string message, string command, Arguments args) + { + int startId=10001; + int endId=19030; + int[] additionalItems = new int[] { }; + string itemTypeName = "helmets"; + return HandleGetAllSelectedItems(sender, target, message, command, args, startId, endId, additionalItems, itemTypeName); + } + + /// + /// Adds all available dolls to target's inventory. + /// + /// + /// + /// + /// + /// + /// + private CommandResult HandleGetAllDolls(Character sender, Character target, string message, string command, Arguments args) + { + int startId=900001; + int endId=900004; + int[] additionalItems = new int[] { }; + string itemTypeName = "dolls"; + return HandleGetAllSelectedItems(sender, target, message, command, args, startId, endId, additionalItems, itemTypeName); + } + + + /// + /// Remove all available gendered items from target's inventory. + /// + /// + /// + /// + /// + /// + /// + private CommandResult HandleRemoveAllFromGender(Character sender, Character target, string message, string command, Arguments args) + { + string itemTypeName = "item"; + var addedCount = 0; + + string strCompare = sender.Gender == Gender.Male ? "(Female)" : "(Male)"; + + foreach (var item in sender.Inventory.GetItems()) + { + + Log.Warning(item.Value.Data.Name); + var itemIsToBeDeleted = item.Value.Data.Name.Contains(strCompare, StringComparison.InvariantCultureIgnoreCase); + if(itemIsToBeDeleted) + { + sender.Inventory.Remove(item.Value, 1, InventoryItemRemoveMsg.Destroyed); + addedCount++; + } + }; + if (sender == target) + { + sender.ServerMessage(Localization.Get("Removed {0} {1} from your inventory."), addedCount, itemTypeName); + } + else + { + target.ServerMessage(Localization.Get("{1} removed {0} {2} from your inventory."), addedCount, sender.TeamName, itemTypeName); + sender.ServerMessage(Localization.Get("Removed {0} {1} to target's inventory."), addedCount, itemTypeName); + } + + return CommandResult.Okay; + } + + /// + /// Adds all requested and availabled items to target's inventory. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + private CommandResult HandleGetAllSelectedItems(Character sender, Character target, string message, string command, Arguments args, int startId, int endId, int[] additionalItems, string itemTypeName) { var addedCount = 0; - for (var itemId = 628001; itemId <= 629503; ++itemId) + for (var itemId = startId; itemId <= endId; ++itemId) + { + if (!ZoneServer.Instance.Data.ItemDb.Contains(itemId)) + continue; + + if (!sender.Inventory.HasItem(itemId)) + { + sender.Inventory.Add(new Item(itemId), InventoryAddType.PickUp); + addedCount++; + } + } + + // for each additional item + foreach (var itemId in additionalItems) { if (!ZoneServer.Instance.Data.ItemDb.Contains(itemId)) continue; @@ -701,12 +989,12 @@ private CommandResult HandleGetAllHats(Character sender, Character target, strin if (sender == target) { - sender.ServerMessage(Localization.Get("Added {0} hats to your inventory."), addedCount); + sender.ServerMessage(Localization.Get("Added {0} {1} to your inventory."), addedCount, itemTypeName); } else { - target.ServerMessage(Localization.Get("{1} added {0} hats to your inventory."), addedCount, sender.TeamName); - sender.ServerMessage(Localization.Get("Added {0} hats to target's inventory."), addedCount); + target.ServerMessage(Localization.Get("{1} added {0} {2} to your inventory."), addedCount, sender.TeamName, itemTypeName); + sender.ServerMessage(Localization.Get("Added {0} {1} to target's inventory."), addedCount, itemTypeName); } return CommandResult.Okay; diff --git a/src/ZoneServer/ZoneServer.cs b/src/ZoneServer/ZoneServer.cs index 84e7cdc59..1dd849778 100644 --- a/src/ZoneServer/ZoneServer.cs +++ b/src/ZoneServer/ZoneServer.cs @@ -101,7 +101,11 @@ public override void Run(string[] args) var title = string.Format("Zone ({0}, {1})", groupId, serverId); ConsoleUtil.WriteHeader(ConsoleHeader.ProjectName, title, ConsoleColor.DarkGreen, ConsoleHeader.Logo, ConsoleHeader.Credits); - ConsoleUtil.LoadingTitle(); + + // Skip the following command on incompatible systems + if (Environment.OSVersion.Platform == PlatformID.Win32NT) + ConsoleUtil.LoadingTitle(); + // Set up zone server specific logging or we might run into // issues with multiple servers trying to write files at the @@ -124,8 +128,21 @@ public override void Run(string[] args) this.StartCommunicator(); this.StartAcceptor(); - ConsoleUtil.RunningTitle(); - new ConsoleCommands().Wait(); + + // If running on Windows system, enable console inputs + // otherwise, start an event loop to keep server running until stopped + if (Environment.OSVersion.Platform == PlatformID.Win32NT) + { + ConsoleUtil.RunningTitle(); + new ConsoleCommands().Wait(); + } + else + { + while (true) + { + System.Threading.Thread.Sleep(5000); + } + } } /// diff --git a/start b/start new file mode 100755 index 000000000..0ebc9e0f8 --- /dev/null +++ b/start @@ -0,0 +1,56 @@ +#!/bin/sh +# killall -9 BarracksServer +# killall -9 ZoneServer +# killall -9 WebServer +echo "Starting Melia Project..." +SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" +ENVIRONMENT=$1 +FILENAME=$2 +GROUP_ID=$3 +SERVER_ID=$4 +SUBFOLDER="net8.0" + +do_error () { + echo "" + echo $1 + echo "" + exit 1 +} + +run_all() { + cd "$SCRIPTPATH/bin/$ENVIRONMENT/$SUBFOLDER" + for FILEN in BarracksServer ZoneServer WebServer + do + sh -c "./$FILEN $GROUP_ID $SERVER_ID" & + sleep 10 + done + tail -f /dev/null +} + +run_process() { + cd "$SCRIPTPATH/bin/$ENVIRONMENT/$SUBFOLDER" + sh -c "./$FILENAME $GROUP_ID $SERVER_ID" +} + +if [ -z "$ENVIRONMENT" ] +then + ENVIRONMENT="Debug" +fi + +if [ -z "$GROUP_ID" ] +then + GROUP_ID="1001" +fi + +if [ -z "$SERVER_ID" ] +then + SERVER_ID="1" +fi + +if [ -z "$FILENAME" ] +then + run_all +else + run_process +fi + diff --git a/stop b/stop new file mode 100755 index 000000000..8ef4cc2e7 --- /dev/null +++ b/stop @@ -0,0 +1,6 @@ +#!/bin/sh +echo "Stoping Melia Project..." + +killall -9 BarracksServer +killall -9 ZoneServer +killall -9 WebServer diff --git a/system/conf/commands.conf b/system/conf/commands.conf index 9f55aff87..f15f3ca11 100644 --- a/system/conf/commands.conf +++ b/system/conf/commands.conf @@ -93,6 +93,22 @@ spawn : 50,50 // Adds all hats to inventory madhatter : 50,50 +// Adds all {item type} to inventory +getallwings : 50,50 +getalltoys : 50,50 +getallarmbands : 50,50 +getallcostumes : 50,50 +getalllens : 50,50 +getalleffectcostumes : 50,50 +getallspecialcostumes : 50,50 +getallspecialcostumeskins : 50,50 +getallhairs : 50,50 +getallhelmets : 50,50 +getalldolls : 50,50 + +// Remove all items in inventory that are for the other gender +removeallfromgender : 50,50 + // Levels character up levelup : 50,50 diff --git a/system/conf/database.conf b/system/conf/database.conf index 314324f8e..a385611e9 100644 --- a/system/conf/database.conf +++ b/system/conf/database.conf @@ -7,4 +7,4 @@ user : root pass : database : melia -include "/user/conf/database.conf" +include "/user/conf/database.conf" \ No newline at end of file diff --git a/system/conf/web.conf b/system/conf/web.conf index 68a02359c..e06fbc756 100644 --- a/system/conf/web.conf +++ b/system/conf/web.conf @@ -5,6 +5,6 @@ // Path to the PHP CGI executable // If the file doesn't exist, and the configured path is in user/tools/, // PHP is downloaded automatically on Windows. -php_cgi_bin: user/tools/php/php-cgi.exe +php_cgi_exe: /usr/bin/php-cgi include "/user/conf/web.conf" diff --git a/user/conf/database-example-for-docker.conf b/user/conf/database-example-for-docker.conf new file mode 100644 index 000000000..0fc5648f6 --- /dev/null +++ b/user/conf/database-example-for-docker.conf @@ -0,0 +1,8 @@ +// Melia +// Configuration file +//---------------------------------------------------------------------------- + +host : melia-database +user : root +pass : 123456 +database : melia