mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-12-21 11:59:37 -06:00
Compare commits
9 Commits
crashFix
...
vanity-rel
| Author | SHA1 | Date | |
|---|---|---|---|
| 175b354e68 | |||
| 4c3949e5d8 | |||
| f727e3951c | |||
| 4867136133 | |||
| 6e07798023 | |||
| 3dc7b6ef7f | |||
|
|
2c9a98313a | ||
|
|
d1dc9f5403 | ||
|
|
52b5994b98 |
39
README.md
39
README.md
@@ -224,6 +224,44 @@ sudo setcap 'cap_net_bind_service=+ep' AuthServer
|
||||
```
|
||||
and then go to `build/masterconfig.ini` and change `use_sudo_auth` to 0.
|
||||
|
||||
### Linux Service
|
||||
If you are running this on a linux based system, it will use your terminal to run the program interactively, preventing you using it for other tasks and requiring it to be open to run the server.
|
||||
_Note: You could use screen or tmux instead for virtual terminals_
|
||||
To run the server non-interactively, we can use a systemctl service by copying the following file:
|
||||
```shell
|
||||
cp ./systemd.example /etc/systemd/system/darkflame.service
|
||||
```
|
||||
|
||||
Make sure to edit the file in `/etc/systemd/system/darkflame.service` and change the:
|
||||
- `User` and `Group` to the user that runs the darkflame server.
|
||||
- `ExecPath` to the full file path of the server executable.
|
||||
|
||||
To register, enable and start the service use the following commands:
|
||||
|
||||
- Reload the systemd manager configuration to make it aware of the new service file:
|
||||
```shell
|
||||
systemctl daemon-reload
|
||||
```
|
||||
- Start the service:
|
||||
```shell
|
||||
systemctl start darkflame.service
|
||||
```
|
||||
- Enable OR disable the service to start on boot using:
|
||||
```shell
|
||||
systemctl enable darkflame.service
|
||||
systemctl disable darkflame.service
|
||||
```
|
||||
- Verify that the service is running without errors:
|
||||
```shell
|
||||
systemctl status darkflame.service
|
||||
```
|
||||
- You can also restart, stop, or check the logs of the service using journalctl
|
||||
```shell
|
||||
systemctl restart darkflame.service
|
||||
systemctl stop darkflame.service
|
||||
journalctl -xeu darkflame.service
|
||||
```
|
||||
|
||||
### First admin user
|
||||
Run `MasterServer -a` to get prompted to create an admin account. This method is only intended for the system administrator as a means to get started, do NOT use this method to create accounts for other users!
|
||||
|
||||
@@ -285,6 +323,7 @@ Below are known good SHA256 checksums of the client:
|
||||
* `0d862f71eedcadc4494c4358261669721b40b2131101cbd6ef476c5a6ec6775b` (unpacked client, includes extra locales, rar compressed)
|
||||
|
||||
If the returned hash matches one of the lines above then you can continue with setting up the server. If you are using a fully downloaded and complete client from live, then it will work, but the hash above may not match. Otherwise you must obtain a full install of LEGO® Universe 1.10.64.
|
||||
You must also make absolutely sure your LEGO Universe client is not in a Windows OneDrive. DLU is not and will not support a client being stored in a OneDrive, so ensure you have moved the client outside of that location.
|
||||
|
||||
### Darkflame Universe Client
|
||||
Darkflame Universe clients identify themselves using a higher version number than the regular live clients out there.
|
||||
|
||||
@@ -5610,10 +5610,18 @@ void GameMessages::HandleModularBuildFinish(RakNet::BitStream* inStream, Entity*
|
||||
std::vector<LDFBaseData*> config;
|
||||
config.push_back(moduleAssembly);
|
||||
|
||||
LWOOBJID newIdBig;
|
||||
// Make sure a subkey isnt already in use. Persistent Ids do not make sense here since this only needs to be unique for
|
||||
// this character. Because of that, we just generate a random id and check for a collision.
|
||||
do {
|
||||
newIdBig = ObjectIDManager::Instance()->GenerateRandomObjectID();
|
||||
GeneralUtils::SetBit(newIdBig, eObjectBits::CHARACTER);
|
||||
} while (inv->FindItemBySubKey(newIdBig));
|
||||
|
||||
if (count == 3) {
|
||||
inv->AddItem(6416, 1, eLootSourceType::QUICKBUILD, eInventoryType::MODELS, config);
|
||||
inv->AddItem(6416, 1, eLootSourceType::QUICKBUILD, eInventoryType::MODELS, config, LWOOBJID_EMPTY, true, false, newIdBig);
|
||||
} else if (count == 7) {
|
||||
inv->AddItem(8092, 1, eLootSourceType::QUICKBUILD, eInventoryType::MODELS, config);
|
||||
inv->AddItem(8092, 1, eLootSourceType::QUICKBUILD, eInventoryType::MODELS, config, LWOOBJID_EMPTY, true, false, newIdBig);
|
||||
}
|
||||
|
||||
auto* missionComponent = character->GetComponent<MissionComponent>();
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
#include "Logger.h"
|
||||
#include "BinaryPathFinder.h"
|
||||
#include "EntityInfo.h"
|
||||
#include "Spawner.h"
|
||||
#include "dZoneManager.h"
|
||||
#include "../dWorldServer/ObjectIDManager.h"
|
||||
#include "Level.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
@@ -29,6 +33,21 @@ void VanityUtilities::SpawnVanity() {
|
||||
|
||||
const uint32_t zoneID = Game::server->GetZoneID();
|
||||
|
||||
for (const auto& npc : m_NPCs) {
|
||||
if (npc.m_ID == LWOOBJID_EMPTY) continue;
|
||||
if (npc.m_LOT == 176){
|
||||
Game::zoneManager->RemoveSpawner(npc.m_ID);
|
||||
} else{
|
||||
auto* entity = Game::entityManager->GetEntity(npc.m_ID);
|
||||
if (!entity) continue;
|
||||
entity->Smash(LWOOBJID_EMPTY, eKillType::VIOLENT);
|
||||
}
|
||||
}
|
||||
|
||||
m_NPCs.clear();
|
||||
m_Parties.clear();
|
||||
m_PartyPhrases.clear();
|
||||
|
||||
ParseXML((BinaryPathFinder::GetBinaryDir() / "vanity/NPC.xml").string());
|
||||
|
||||
// Loop through all parties
|
||||
@@ -53,7 +72,7 @@ void VanityUtilities::SpawnVanity() {
|
||||
|
||||
// Loop through all locations
|
||||
for (const auto& location : party.m_Locations) {
|
||||
rate = GeneralUtils::GenerateRandomNumber<float>(0, 1);
|
||||
rate = GeneralUtils::GenerateRandomNumber<float>(0, 1);
|
||||
if (0.75f < rate) {
|
||||
continue;
|
||||
}
|
||||
@@ -66,10 +85,11 @@ void VanityUtilities::SpawnVanity() {
|
||||
}
|
||||
|
||||
auto& npc = npcList[npcIndex];
|
||||
|
||||
// Skip spawners
|
||||
if (npc.m_LOT == 176) continue;
|
||||
|
||||
taken.push_back(npcIndex);
|
||||
|
||||
// Spawn the NPC
|
||||
LOG("ldf size is %i", npc.ldf.size());
|
||||
if (npc.ldf.empty()) {
|
||||
npc.ldf = {
|
||||
@@ -79,13 +99,16 @@ void VanityUtilities::SpawnVanity() {
|
||||
}
|
||||
|
||||
// Spawn the NPC
|
||||
auto* npcEntity = SpawnNPC(npc.m_LOT, npc.m_Name, location.m_Position, location.m_Rotation, npc.m_Equipment, npc.ldf);
|
||||
if (!npc.m_Phrases.empty()) {
|
||||
npcEntity->SetVar<std::vector<std::string>>(u"chats", m_PartyPhrases);
|
||||
SetupNPCTalk(npcEntity);
|
||||
if (npc.m_LOT == 176){
|
||||
npc.m_ID = SpawnSpawner(npc.m_LOT, location.m_Position, location.m_Rotation, npc.ldf);
|
||||
} else {
|
||||
auto* npcEntity = SpawnNPC(npc.m_LOT, npc.m_Name, location.m_Position, location.m_Rotation, npc.m_Equipment, npc.ldf);
|
||||
if (!npc.m_Phrases.empty()) {
|
||||
npcEntity->SetVar<std::vector<std::string>>(u"chats", m_PartyPhrases);
|
||||
SetupNPCTalk(npcEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -111,23 +134,28 @@ void VanityUtilities::SpawnVanity() {
|
||||
new LDFData<std::u16string>(u"custom_script_client", u"scripts\\ai\\SPEC\\MISSION_MINIGAME_CLIENT.lua")
|
||||
};
|
||||
}
|
||||
if (npc.m_LOT == 176){
|
||||
npc.m_ID = SpawnSpawner(npc.m_LOT, location.m_Position, location.m_Rotation, npc.ldf);
|
||||
} else {
|
||||
// Spawn the NPC
|
||||
auto* npcEntity = SpawnNPC(npc.m_LOT, npc.m_Name, location.m_Position, location.m_Rotation, npc.m_Equipment, npc.ldf);
|
||||
if (!npcEntity) continue;
|
||||
npc.m_ID = npcEntity->GetObjectID();
|
||||
if (!npc.m_Phrases.empty()){
|
||||
npcEntity->SetVar<std::vector<std::string>>(u"chats", npc.m_Phrases);
|
||||
|
||||
// Spawn the NPC
|
||||
auto* npcEntity = SpawnNPC(npc.m_LOT, npc.m_Name, location.m_Position, location.m_Rotation, npc.m_Equipment, npc.ldf);
|
||||
if (!npc.m_Phrases.empty()){
|
||||
npcEntity->SetVar<std::vector<std::string>>(u"chats", npc.m_Phrases);
|
||||
auto* scriptComponent = npcEntity->GetComponent<ScriptComponent>();
|
||||
|
||||
auto* scriptComponent = npcEntity->GetComponent<ScriptComponent>();
|
||||
if (scriptComponent && !npc.m_Script.empty()) {
|
||||
scriptComponent->SetScript(npc.m_Script);
|
||||
scriptComponent->SetSerialized(false);
|
||||
|
||||
if (scriptComponent && !npc.m_Script.empty()) {
|
||||
scriptComponent->SetScript(npc.m_Script);
|
||||
scriptComponent->SetSerialized(false);
|
||||
|
||||
for (const auto& npc : npc.m_Flags) {
|
||||
npcEntity->SetVar<bool>(GeneralUtils::ASCIIToUTF16(npc.first), npc.second);
|
||||
for (const auto& npc : npc.m_Flags) {
|
||||
npcEntity->SetVar<bool>(GeneralUtils::ASCIIToUTF16(npc.first), npc.second);
|
||||
}
|
||||
}
|
||||
SetupNPCTalk(npcEntity);
|
||||
}
|
||||
SetupNPCTalk(npcEntity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,8 +177,21 @@ void VanityUtilities::SpawnVanity() {
|
||||
}
|
||||
}
|
||||
|
||||
Entity* VanityUtilities::SpawnNPC(LOT lot, const std::string& name, const NiPoint3& position,
|
||||
const NiQuaternion& rotation, const std::vector<LOT>& inventory, const std::vector<LDFBaseData*>& ldf) {
|
||||
LWOOBJID VanityUtilities::SpawnSpawner(LOT lot, const NiPoint3& position, const NiQuaternion& rotation, const std::vector<LDFBaseData*>& ldf){
|
||||
SceneObject obj;
|
||||
obj.lot = lot;
|
||||
// guratantee we have no collisions
|
||||
do {
|
||||
obj.id = ObjectIDManager::Instance()->GenerateObjectID();
|
||||
} while(Game::zoneManager->GetSpawner(obj.id));
|
||||
obj.position = position;
|
||||
obj.rotation = rotation;
|
||||
obj.settings = ldf;
|
||||
Level::MakeSpawner(obj);
|
||||
return obj.id;
|
||||
}
|
||||
|
||||
Entity* VanityUtilities::SpawnNPC(LOT lot, const std::string& name, const NiPoint3& position, const NiQuaternion& rotation, const std::vector<LOT>& inventory, const std::vector<LDFBaseData*>& ldf) {
|
||||
EntityInfo info;
|
||||
info.lot = lot;
|
||||
info.pos = position;
|
||||
|
||||
@@ -13,6 +13,7 @@ struct VanityNPCLocation
|
||||
|
||||
struct VanityNPC
|
||||
{
|
||||
LWOOBJID m_ID = LWOOBJID_EMPTY;
|
||||
std::string m_Name;
|
||||
LOT m_LOT;
|
||||
std::vector<LOT> m_Equipment;
|
||||
@@ -44,6 +45,13 @@ public:
|
||||
const std::vector<LDFBaseData*>& ldf
|
||||
);
|
||||
|
||||
static LWOOBJID SpawnSpawner(
|
||||
LOT lot,
|
||||
const NiPoint3& position,
|
||||
const NiQuaternion& rotation,
|
||||
const std::vector<LDFBaseData*>& ldf
|
||||
);
|
||||
|
||||
static std::string ParseMarkdown(
|
||||
const std::string& file
|
||||
);
|
||||
|
||||
@@ -154,7 +154,7 @@ int main(int argc, char** argv) {
|
||||
Game::assetManager = new AssetManager(clientPath);
|
||||
} catch (std::runtime_error& ex) {
|
||||
LOG("Got an error while setting up assets: %s", ex.what());
|
||||
|
||||
LOG("Is the provided client_location in Windows Onedrive? If so, remove it from Onedrive.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,76 @@ Level::~Level() {
|
||||
}
|
||||
}
|
||||
|
||||
void Level::MakeSpawner(SceneObject obj){
|
||||
SpawnerInfo spawnInfo = SpawnerInfo();
|
||||
SpawnerNode* node = new SpawnerNode();
|
||||
spawnInfo.templateID = obj.lot;
|
||||
spawnInfo.spawnerID = obj.id;
|
||||
spawnInfo.templateScale = obj.scale;
|
||||
node->position = obj.position;
|
||||
node->rotation = obj.rotation;
|
||||
node->config = obj.settings;
|
||||
spawnInfo.nodes.push_back(node);
|
||||
for (LDFBaseData* data : obj.settings) {
|
||||
if (data) {
|
||||
if (data->GetKey() == u"spawntemplate") {
|
||||
spawnInfo.templateID = std::stoi(data->GetValueAsString());
|
||||
}
|
||||
|
||||
if (data->GetKey() == u"spawner_node_id") {
|
||||
node->nodeID = std::stoi(data->GetValueAsString());
|
||||
}
|
||||
|
||||
if (data->GetKey() == u"spawner_name") {
|
||||
spawnInfo.name = data->GetValueAsString();
|
||||
}
|
||||
|
||||
if (data->GetKey() == u"max_to_spawn") {
|
||||
spawnInfo.maxToSpawn = std::stoi(data->GetValueAsString());
|
||||
}
|
||||
|
||||
if (data->GetKey() == u"spawner_active_on_load") {
|
||||
spawnInfo.activeOnLoad = std::stoi(data->GetValueAsString());
|
||||
}
|
||||
|
||||
if (data->GetKey() == u"active_on_load") {
|
||||
spawnInfo.activeOnLoad = std::stoi(data->GetValueAsString());
|
||||
}
|
||||
|
||||
if (data->GetKey() == u"respawn") {
|
||||
if (data->GetValueType() == eLDFType::LDF_TYPE_FLOAT) // Floats are in seconds
|
||||
{
|
||||
spawnInfo.respawnTime = std::stof(data->GetValueAsString());
|
||||
} else if (data->GetValueType() == eLDFType::LDF_TYPE_U32) // Ints are in ms?
|
||||
{
|
||||
spawnInfo.respawnTime = std::stoul(data->GetValueAsString()) / 1000;
|
||||
}
|
||||
}
|
||||
if (data->GetKey() == u"spawnsGroupOnSmash") {
|
||||
spawnInfo.spawnsOnSmash = std::stoi(data->GetValueAsString());
|
||||
}
|
||||
if (data->GetKey() == u"spawnNetNameForSpawnGroupOnSmash") {
|
||||
spawnInfo.spawnOnSmashGroupName = data->GetValueAsString();
|
||||
}
|
||||
if (data->GetKey() == u"groupID") { // Load object groups
|
||||
std::string groupStr = data->GetValueAsString();
|
||||
spawnInfo.groups = GeneralUtils::SplitString(groupStr, ';');
|
||||
spawnInfo.groups.erase(spawnInfo.groups.end() - 1);
|
||||
}
|
||||
if (data->GetKey() == u"no_auto_spawn") {
|
||||
spawnInfo.noAutoSpawn = static_cast<LDFData<bool>*>(data)->GetValue();
|
||||
}
|
||||
if (data->GetKey() == u"no_timed_spawn") {
|
||||
spawnInfo.noTimedSpawn = static_cast<LDFData<bool>*>(data)->GetValue();
|
||||
}
|
||||
if (data->GetKey() == u"spawnActivator") {
|
||||
spawnInfo.spawnActivator = static_cast<LDFData<bool>*>(data)->GetValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
Game::zoneManager->MakeSpawner(spawnInfo);
|
||||
}
|
||||
|
||||
const void Level::PrintAllObjects() {
|
||||
for (std::map<uint32_t, Header>::iterator it = m_ChunkHeaders.begin(); it != m_ChunkHeaders.end(); ++it) {
|
||||
if (it->second.id == Level::ChunkTypeID::SceneObjectData) {
|
||||
@@ -230,74 +300,7 @@ void Level::ReadSceneObjectDataChunk(std::istream& file, Header& header) {
|
||||
}
|
||||
|
||||
if (obj.lot == 176) { //Spawner
|
||||
SpawnerInfo spawnInfo = SpawnerInfo();
|
||||
SpawnerNode* node = new SpawnerNode();
|
||||
spawnInfo.templateID = obj.lot;
|
||||
spawnInfo.spawnerID = obj.id;
|
||||
spawnInfo.templateScale = obj.scale;
|
||||
node->position = obj.position;
|
||||
node->rotation = obj.rotation;
|
||||
node->config = obj.settings;
|
||||
spawnInfo.nodes.push_back(node);
|
||||
for (LDFBaseData* data : obj.settings) {
|
||||
if (data) {
|
||||
if (data->GetKey() == u"spawntemplate") {
|
||||
spawnInfo.templateID = std::stoi(data->GetValueAsString());
|
||||
}
|
||||
|
||||
if (data->GetKey() == u"spawner_node_id") {
|
||||
node->nodeID = std::stoi(data->GetValueAsString());
|
||||
}
|
||||
|
||||
if (data->GetKey() == u"spawner_name") {
|
||||
spawnInfo.name = data->GetValueAsString();
|
||||
}
|
||||
|
||||
if (data->GetKey() == u"max_to_spawn") {
|
||||
spawnInfo.maxToSpawn = std::stoi(data->GetValueAsString());
|
||||
}
|
||||
|
||||
if (data->GetKey() == u"spawner_active_on_load") {
|
||||
spawnInfo.activeOnLoad = std::stoi(data->GetValueAsString());
|
||||
}
|
||||
|
||||
if (data->GetKey() == u"active_on_load") {
|
||||
spawnInfo.activeOnLoad = std::stoi(data->GetValueAsString());
|
||||
}
|
||||
|
||||
if (data->GetKey() == u"respawn") {
|
||||
if (data->GetValueType() == eLDFType::LDF_TYPE_FLOAT) // Floats are in seconds
|
||||
{
|
||||
spawnInfo.respawnTime = std::stof(data->GetValueAsString());
|
||||
} else if (data->GetValueType() == eLDFType::LDF_TYPE_U32) // Ints are in ms?
|
||||
{
|
||||
spawnInfo.respawnTime = std::stoul(data->GetValueAsString()) / 1000;
|
||||
}
|
||||
}
|
||||
if (data->GetKey() == u"spawnsGroupOnSmash") {
|
||||
spawnInfo.spawnsOnSmash = std::stoi(data->GetValueAsString());
|
||||
}
|
||||
if (data->GetKey() == u"spawnNetNameForSpawnGroupOnSmash") {
|
||||
spawnInfo.spawnOnSmashGroupName = data->GetValueAsString();
|
||||
}
|
||||
if (data->GetKey() == u"groupID") { // Load object groups
|
||||
std::string groupStr = data->GetValueAsString();
|
||||
spawnInfo.groups = GeneralUtils::SplitString(groupStr, ';');
|
||||
spawnInfo.groups.erase(spawnInfo.groups.end() - 1);
|
||||
}
|
||||
if (data->GetKey() == u"no_auto_spawn") {
|
||||
spawnInfo.noAutoSpawn = static_cast<LDFData<bool>*>(data)->GetValue();
|
||||
}
|
||||
if (data->GetKey() == u"no_timed_spawn") {
|
||||
spawnInfo.noTimedSpawn = static_cast<LDFData<bool>*>(data)->GetValue();
|
||||
}
|
||||
if (data->GetKey() == u"spawnActivator") {
|
||||
spawnInfo.spawnActivator = static_cast<LDFData<bool>*>(data)->GetValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
Spawner* spawner = new Spawner(spawnInfo);
|
||||
Game::zoneManager->AddSpawner(obj.id, spawner);
|
||||
MakeSpawner(obj);
|
||||
} else { //Regular object
|
||||
EntityInfo info;
|
||||
info.spawnerID = 0;
|
||||
|
||||
@@ -53,6 +53,8 @@ public:
|
||||
public:
|
||||
Level(Zone* parentZone, const std::string& filepath);
|
||||
~Level();
|
||||
|
||||
static void MakeSpawner(SceneObject obj);
|
||||
|
||||
const void PrintAllObjects();
|
||||
|
||||
|
||||
19
systemd.example
Normal file
19
systemd.example
Normal file
@@ -0,0 +1,19 @@
|
||||
[Unit]
|
||||
# Description of the service.
|
||||
Description=Darkflame LEGO Universe Server
|
||||
# Wait for network to start first before starting this service.
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
# Services should have their own, dedicated, user account.
|
||||
# The specific user that our service will run as, you can find your current user by issuing `id -un`
|
||||
User=darkflame
|
||||
# The specific group that our service will run as, you can find your current primary group by issuing `id -gn`
|
||||
Group=darkflame
|
||||
# Full Path to the darkflame server process
|
||||
ExecStart=/PATH/TO/DarkflameServer/build/MasterServer
|
||||
|
||||
[Install]
|
||||
# Define the behavior if the service is enabled or disabled to automatically started at boot.
|
||||
WantedBy=multi-user.target
|
||||
Reference in New Issue
Block a user