Compare commits

..

24 Commits

Author SHA1 Message Date
EmosewaMC
6efe2bbeb5 updates 2025-01-01 16:17:59 -08:00
jadebenn
71baa5ce90 feat: Replace calls to system function in server startups (#1691)
* replace linux calls

* windows api

* log child PIDs in parent process

* fix typo for windows

* functions now return the process ID

* use wchar_t for windows APIs

* Update Start.cpp

Try to fix MacOS issues

* Conditionally include unistd.h

* remove sudo config option and add error message for linux

* fix windows .exe extension

* REALLY fix windows

* try replacing c_str() with data()

* really REALLY fix Windows

* Update dNet/dServer.cpp

Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>

* Update dServer.cpp
2025-01-01 15:41:21 -08:00
David Markowitz
5ccd15a7d8 fix first attack being weaponless (#1709) 2025-01-01 13:33:20 -06:00
David Markowitz
35bcaf6e95 Note required g++ compiler version (#1711) 2025-01-01 11:11:37 -08:00
David Markowitz
900c9b6abe fix: add missing racing scripts (#1708) 2025-01-01 10:54:21 -08:00
David Markowitz
94e7cfc211 fix optional (#1707) 2025-01-01 04:07:44 -06:00
David Markowitz
021db0ecd1 add child loading (#1706)
Tested that the NT combat challenge, am skullkin towers and qa wall in avant gardens all function as before.
2025-01-01 00:46:00 -06:00
David Markowitz
0b261e934f fix shooting gallery bugs (#1702) 2024-12-29 18:21:22 -06:00
David Markowitz
1b9f7e44c7 remove dead loop (#1700) 2024-12-28 17:11:44 -06:00
David Markowitz
08a168de88 update cdclient.fdb file check (#1699) 2024-12-27 22:15:32 -06:00
David Markowitz
0c948a8df6 use simpler converter (#1695) 2024-12-24 22:23:14 -08:00
Gie "Max" Vanommeslaeghe
6ed6efa921 Merge pull request #1694 from DarkflameUniverse/latin1
fix: use encoding on latin1 strings from cdclient
2024-12-25 00:27:00 +01:00
Gie "Max" Vanommeslaeghe
dcc9e023a6 Merge pull request #1693 from DarkflameUniverse/bandwidth
fix: remove bandwidth limit
2024-12-25 00:26:51 +01:00
Gie "Max" Vanommeslaeghe
8509ec8856 Merge pull request #1692 from DarkflameUniverse/really
fix: folder and file checks
2024-12-25 00:25:48 +01:00
David Markowitz
18295017c1 use encoding
use template function

Update GeneralUtils.cpp

consolidate duplicate code

Update GeneralUtils.cpp

Update BinaryIO.cpp

compilers
2024-12-24 14:32:08 -08:00
David Markowitz
e8f011b830 Update sharedconfig.ini 2024-12-24 13:07:55 -08:00
David Markowitz
a787673baf show error box for windows 2024-12-24 12:57:20 -08:00
David Markowitz
e869c0ad03 Add parenthesis around path 2024-12-24 12:39:18 -08:00
David Markowitz
b2af3fa9d4 use binary dir paths, create ones that dont exist, do not run if critical ones do not exist. 2024-12-24 12:36:54 -08:00
David Markowitz
2560bb00da feat: add ns race server script and ignore 3 scripts from pet cove (#1682)
* brother

* use some better logic

* Implement spider boss msg script

tested that the message now shows up when hitting the survival spider entrance area

* add drag to start race feature

* ignore 3 more scripts

* add Ns race server script

* remove logs

* unique

* Update RaceImaginationServer.cpp

* Update CppScripts.cpp
2024-12-20 01:59:22 -06:00
David Markowitz
1ae21c423f skip non-files (#1690) 2024-12-19 12:19:41 -06:00
jadebenn
0ae9eb4a96 remove unneeded Component.cpp, forward declare dependencies, and make Component definition header-only (#1688) 2024-12-18 00:45:56 -08:00
David Markowitz
fced6d753a fix: Create resServer and logs if it doesnt exist and update readme (#1686)
* create resServer if not exist

* Update README.md

* Update README.md
2024-12-17 21:06:07 -06:00
David Markowitz
15dc5feeb5 feat: start car races if you "equip" the car near the car pad; add more old ns scripts to ignore list (#1681)
* brother

* use some better logic

* Implement spider boss msg script

tested that the message now shows up when hitting the survival spider entrance area

* add drag to start race feature
2024-12-17 21:04:35 -06:00
70 changed files with 1303 additions and 533 deletions

View File

@@ -1,5 +1,5 @@
PROJECT_VERSION_MAJOR=2
PROJECT_VERSION_MINOR=3
PROJECT_VERSION_MAJOR=3
PROJECT_VERSION_MINOR=0
PROJECT_VERSION_PATCH=0
# Debugging

View File

@@ -24,13 +24,16 @@ Darkflame Universe is a server emulator and does not distribute any LEGO® Unive
Warning: WSL version 1 does NOT support using sqlite as a database due to how it handles filesystem synchronization.
You must use Version 2 if you must run the server under WSL. Not doing so will result in save data loss.
* Single player installs now no longer require building the server from source or installing development tools.
* Download the [latest release](https://github.com/DarkflameUniverse/DarkflameServer/releases) and extract the files into a folder inside your client.
* You should be able to see the folder with the server executables in the same folder as `legouniverse.exe`.
* Open `sharedconfig.ini` and find the line that says `client_location` and put `..` after it so the line reads `client_location=..`.
* Download the [latest windows release](https://github.com/DarkflameUniverse/DarkflameServer/releases) (or whichever release you need) and extract the files into a folder inside your client. Note that this setup is expecting that when double clicking the folder that you put in the same folder as `legouniverse.exe`, the file `MasterServer.exe` is in there.
* You should be able to see the folder with the server files in the same folder as `legouniverse.exe`.
* Go into the server files folder and open `sharedconfig.ini`. Find the line that says `client_location` and put `..` after it so the line reads `client_location=..`.
* To run the server, double-click `MasterServer.exe`.
* You will be asked to create an account the first time you run the server.
* You will be asked to create an account the first time you run the server. After you have created the account, the server will shutdown and need to be restarted.
* To connect to the server, either delete the file `boot.cfg` which is found in your LEGO Universe client, rename the file `boot.cfg` to something else or follow the steps [here](#allowing-a-user-to-connect-to-your-server) if you wish to keep the file.
* When shutting down the server, it is highly recommended to click the `MasterServer.exe` window and hold `ctrl` while pressing `c` to stop the server.
* We are working on a way to make it so when you close the game, the server saves automatically alongside when you open the game, the server starts automatically.
* We are working on a way to make it so when you close the game, the server stops automatically alongside when you open the game, the server starts automatically.
* If you are not setting a server up on mac, you can ignore this note
* Note: you'll need to allow through System Preferences `AuthServer`, `ChatServer`, `MasterServer`, `WorldServer` and `libmariadbcpp.dylib` to run. The initial pop-up will block it due to the binaries being unsigned, after allowing them to run the servers will run as normal.
<font size="32">**If you are not planning on hosting a server for others, working in the codebase or wanting to use MariaDB for a database, you can stop reading here.**</font>
@@ -67,6 +70,12 @@ git clone --recursive https://github.com/DarkflameUniverse/DarkflameServer
## Install dependencies
### Required compiler versions
- g++11 or greater
- MSVC unchecked
- clang unchecked
- appleclang unchecked
### Windows packages
Ensure that you have either the [MSVC C++ compiler](https://visualstudio.microsoft.com/vs/features/cplusplus/) (recommended) or the [Clang compiler](https://github.com/llvm/llvm-project/releases/) installed.
You'll also need to download and install [CMake](https://cmake.org/download/) (version <font size="4">**CMake version 3.25**</font> or later!).
@@ -201,6 +210,7 @@ If you would like to build the server faster, append `-j<number>` where number i
### Notes
Depending on your operating system, you may need to adjust some pre-processor defines in [CMakeVariables.txt](./CMakeVariables.txt) before building:
* If you are on MacOS, ensure OPENSSL_ROOT_DIR is pointing to the openssl root directory.
* By default it should be set to the correct directory.
* If you are using a Darkflame Universe client, ensure `client_net_version` in `build/sharedconfig.ini` is changed to 171023.
## Configuring your server
@@ -223,28 +233,41 @@ Navigate to `build/sharedconfig.ini` and fill in the following fields:
* `chatconfig.ini` contains a port option.
* `masterconfig.ini` contains options related to permissions you want to run your servers with.
* `sharedconfig.ini` contains several options that are shared across all servers
* `worldconfig.ini` contains several options to turn on QOL improvements should you want them. If you would like the most vanilla experience possible, you will need to turn some of these settings off.
* `worldconfig.ini` contains several options to turn on Quality of Life improvements should you want them. If you would like the most vanilla experience possible, you will need to turn some of these settings off.
## Verify your setup
Your build directory should now look like this:
* AuthServer
* ChatServer
* MasterServer
* WorldServer
* authconfig.ini
* chatconfig.ini
* masterconfig.ini
Your build directory should contain at a minimum all of the following files.
All listed files are required for a server to start.
`ini` files can be located at the environment variable `DLU_CONFIG_DIR` and do not need to be located in this directory.
(windows will have .exe at the end of the executables):
* sharedconfig.ini
* AuthServer(.exe)
* authconfig.ini
* ChatServer(.exe)
* chatconfig.ini
* MasterServer(.exe)
* masterconfig.ini
* WorldServer(.exe)
* worldconfig.ini
* ...
* blocklist.dcf
* migrations
* vanity
* navmeshes
* 1 of the following lists based on platform
* windows
* libmariadb.dll
* mariadbcpp.dll
* zlib.dll
* MacOS
* libmariadbcpp.dylib
* *nix
* libmariadbcpp.so
## Running the server
If everything has been configured correctly you should now be able to run the `MasterServer` binary which is located in the `build` directory. Darkflame Universe utilizes port numbers under 1024, so under Linux you either have to give the `AuthServer` binary network permissions or run it under sudo.
To give `AuthServer` network permissions and not require sudo, run the following command
If everything has been configured correctly you should now be able to run the `MasterServer` binary which is located in the `build` directory. Darkflame Universe utilizes port numbers under 1024, so under Linux you have to give the `AuthServer` binary network permissions by running the following command:
```bash
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.
@@ -307,8 +330,14 @@ To connect to a server follow these steps:
* Replace the contents after to `:` and the following `,` with what you configured as the server's public facing IP. For example `AUTHSERVERIP=0:localhost` for locally hosted servers
* Next locate the line `UGCUSE3DSERVICES=7:`
* Ensure the number after the 7 is a `0`
* Alternatively, remove the line with `UGCUSE3DSERVICES` altogether
* Launch `legouniverse.exe`, through `wine` if on a Unix-like operating system
* Note that if you are on WSL2, you will need to configure the public IP in the server and client to be the IP of the WSL2 instance and not localhost, which can be found by running `ifconfig` in the terminal. Windows defaults to WSL1, so this will not apply to most users.
As an example, here is what the boot.cfg is required to contain for a server with the ip 12.34.56.78
```cfg
AUTHSERVERIP=0:12.34.56.78,
UGCUSE3DSERVICES=7:0
```
## Updating your server
To update your server to the latest version navigate to your cloned directory

View File

@@ -27,7 +27,6 @@
#include "Game.h"
#include "Server.h"
namespace Game {
Logger* logger = nullptr;
dServer* server = nullptr;

View File

@@ -1,7 +1,4 @@
add_executable(AuthServer "AuthServer.cpp")
target_link_libraries(AuthServer ${COMMON_LIBRARIES} dServer)
target_include_directories(AuthServer PRIVATE ${PROJECT_SOURCE_DIR}/dServer)
add_compile_definitions(AuthServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}\"")

View File

@@ -2,16 +2,25 @@
#include <string>
//For reading null-terminated strings
std::string BinaryIO::ReadString(std::istream& instream) {
std::string toReturn;
char buffer;
template<typename StringType>
StringType ReadString(std::istream& instream) {
StringType toReturn{};
typename StringType::value_type buffer{};
BinaryIO::BinaryRead(instream, buffer);
while (buffer != 0x00) {
toReturn += buffer;
BinaryRead(instream, buffer);
BinaryIO::BinaryRead(instream, buffer);
}
return toReturn;
}
std::string BinaryIO::ReadString(std::istream& instream) {
return ::ReadString<std::string>(instream);
}
std::u8string BinaryIO::ReadU8String(std::istream& instream) {
return ::ReadString<std::u8string>(instream);
}

View File

@@ -65,6 +65,8 @@ namespace BinaryIO {
std::string ReadString(std::istream& instream);
std::u8string ReadU8String(std::istream& instream);
inline bool DoesFileExist(const std::string& name) {
std::ifstream f(name.c_str());
return f.good();

View File

@@ -65,13 +65,14 @@ int64_t FdbToSqlite::Convert::ReadInt64(std::istream& cdClientBuffer) {
return value;
}
// cdclient is encoded in latin1
std::string FdbToSqlite::Convert::ReadString(std::istream& cdClientBuffer) {
int32_t prevPosition = SeekPointer(cdClientBuffer);
auto readString = BinaryIO::ReadString(cdClientBuffer);
const auto readString = BinaryIO::ReadU8String(cdClientBuffer);
cdClientBuffer.seekg(prevPosition);
return readString;
return GeneralUtils::Latin1ToUTF8(readString);
}
int32_t FdbToSqlite::Convert::SeekPointer(std::istream& cdClientBuffer) {

View File

@@ -167,6 +167,15 @@ std::u16string GeneralUtils::ASCIIToUTF16(const std::string_view string, const s
return ret;
}
std::string GeneralUtils::Latin1ToUTF8(const std::u8string_view string, const size_t size) {
std::string toReturn{};
for (const auto u : string) {
PushUTF8CodePoint(toReturn, u);
}
return toReturn;
}
//! Converts a (potentially-ill-formed) UTF-16 string to UTF-8
//! See: <http://simonsapin.github.io/wtf-8/#decoding-ill-formed-utf-16>
std::string GeneralUtils::UTF16ToWTF8(const std::u16string_view string, const size_t size) {
@@ -175,9 +184,9 @@ std::string GeneralUtils::UTF16ToWTF8(const std::u16string_view string, const si
ret.reserve(newSize);
for (size_t i = 0; i < newSize; ++i) {
const char16_t u = string[i];
const auto u = string[i];
if (IsLeadSurrogate(u) && (i + 1) < newSize) {
const char16_t next = string[i + 1];
const auto next = string[i + 1];
if (IsTrailSurrogate(next)) {
i += 1;
const char32_t cp = 0x10000
@@ -291,11 +300,12 @@ std::u16string GeneralUtils::ReadWString(RakNet::BitStream& inStream) {
std::vector<std::string> GeneralUtils::GetSqlFileNamesFromFolder(const std::string_view folder) {
// Because we dont know how large the initial number before the first _ is we need to make it a map like so.
std::map<uint32_t, std::string> filenames{};
std::map<uint32_t, std::string> filenames{};
for (const auto& t : std::filesystem::directory_iterator(folder)) {
auto filename = t.path().filename().string();
const auto index = std::stoi(GeneralUtils::SplitString(filename, '_').at(0));
filenames.emplace(index, std::move(filename));
if (t.is_directory() || t.is_symlink()) continue;
auto filename = t.path().filename().string();
const auto index = std::stoi(GeneralUtils::SplitString(filename, '_').at(0));
filenames.emplace(index, std::move(filename));
}
// Now sort the map by the oldest migration.

View File

@@ -51,6 +51,14 @@ namespace GeneralUtils {
bool _NextUTF8Char(std::string_view& slice, uint32_t& out);
}
//! Converts a Latin1 string to a UTF-8 string
/*!
\param string The string to convert
\param size A size to trim the string to. Default is SIZE_MAX (No trimming)
\return An UTF-8 representation of the string
*/
std::string Latin1ToUTF8(const std::u8string_view string, const size_t size = SIZE_MAX);
//! Converts a UTF-16 string to a UTF-8 string
/*!
\param string The string to convert

View File

@@ -957,6 +957,7 @@ namespace MessageType {
MODIFY_PLAYER_ZONE_STATISTIC = 1046,
APPLY_EXTERNAL_FORCE = 1049,
GET_APPLIED_EXTERNAL_FORCE = 1050,
ACTIVITY_NOTIFY = 1051,
ITEM_EQUIPPED = 1052,
ACTIVITY_STATE_CHANGE_REQUEST = 1053,
OVERRIDE_FRICTION = 1054,
@@ -1253,6 +1254,7 @@ namespace MessageType {
VEHICLE_NOTIFY_HIT_EXPLODER = 1385,
CHECK_NEAREST_ROCKET_LAUNCH_PRE_CONDITIONS = 1386,
REQUEST_NEAREST_ROCKET_LAUNCH_PRE_CONDITIONS = 1387,
CONFIGURE_RACING_CONTROL = 1388,
CONFIGURE_RACING_CONTROL_CLIENT = 1389,
NOTIFY_RACING_CLIENT = 1390,
RACING_PLAYER_HACK_CAR = 1391,

View File

@@ -14,7 +14,7 @@ public:
uint32_t lastPlayedTimestamp{};
float primaryScore{};
float secondaryScore{};
uint32_t tertiaryScore{};
float tertiaryScore{};
uint32_t numWins{};
uint32_t numTimesPlayed{};
uint32_t ranking{};

View File

@@ -56,6 +56,7 @@ std::optional<IProperty::PropertyEntranceResult> MySQLDatabase::GetProperties(co
params.playerId
);
if (count->next()) {
if (!result) result = IProperty::PropertyEntranceResult();
result->totalEntriesMatchingQuery = count->getUInt("count");
}
} else {
@@ -109,11 +110,13 @@ std::optional<IProperty::PropertyEntranceResult> MySQLDatabase::GetProperties(co
params.playerSort
);
if (count->next()) {
if (!result) result = IProperty::PropertyEntranceResult();
result->totalEntriesMatchingQuery = count->getUInt("count");
}
}
while (properties->next()) {
if (!result) result = IProperty::PropertyEntranceResult();
auto& entry = result->entries.emplace_back();
entry.id = properties->getUInt64("id");
entry.ownerId = properties->getUInt64("owner_id");

View File

@@ -5,6 +5,7 @@
#include "dConfig.h"
#include "Logger.h"
#include "dPlatforms.h"
#include "BinaryPathFinder.h"
// Static Variables
@@ -17,7 +18,14 @@ namespace {
void SQLiteDatabase::Connect() {
LOG("Using SQLite database");
con = new CppSQLite3DB();
con->open(Game::config->GetValue("sqlite_database_path").c_str());
const auto path = BinaryPathFinder::GetBinaryDir() / Game::config->GetValue("sqlite_database_path");
if (!std::filesystem::exists(path)) {
LOG("Creating sqlite path %s", path.string().c_str());
std::filesystem::create_directories(path.parent_path());
}
con->open(path.string().c_str());
isConnected = true;
// Make sure wal is enabled for the database.

View File

@@ -775,6 +775,12 @@ void Entity::Initialize() {
// Hacky way to trigger these when the object has had a chance to get constructed
AddCallbackTimer(0, [this]() {
this->GetScript()->OnStartup(this);
if (this->m_ParentEntity) {
GameMessages::ChildLoaded childLoaded;
childLoaded.childID = this->m_ObjectID;
childLoaded.templateID = this->GetLOT();
this->m_ParentEntity->OnChildLoaded(childLoaded);
}
});
if (!m_Character && Game::entityManager->GetGhostingEnabled()) {
@@ -1493,6 +1499,18 @@ void Entity::OnChoiceBoxResponse(Entity* sender, int32_t button, const std::u16s
GetScript()->OnChoiceBoxResponse(this, sender, button, buttonIdentifier, identifier);
}
void Entity::OnActivityNotify(GameMessages::ActivityNotify& notify) {
GetScript()->OnActivityNotify(this, notify);
}
void Entity::OnShootingGalleryFire(GameMessages::ShootingGalleryFire& fire) {
GetScript()->OnShootingGalleryFire(*this, fire);
}
void Entity::OnChildLoaded(GameMessages::ChildLoaded& childLoaded) {
GetScript()->OnChildLoaded(*this, childLoaded);
}
void Entity::RequestActivityExit(Entity* sender, LWOOBJID player, bool canceled) {
GetScript()->OnRequestActivityExit(sender, player, canceled);
}

View File

@@ -13,6 +13,12 @@
#include "eKillType.h"
#include "Observable.h"
namespace GameMessages {
struct ActivityNotify;
struct ShootingGalleryFire;
struct ChildLoaded;
};
namespace Loot {
class Info;
};
@@ -210,6 +216,9 @@ public:
void OnZonePropertyModelRemoved(Entity* player);
void OnZonePropertyModelRemovedWhileEquipped(Entity* player);
void OnZonePropertyModelRotated(Entity* player);
void OnActivityNotify(GameMessages::ActivityNotify& notify);
void OnShootingGalleryFire(GameMessages::ShootingGalleryFire& notify);
void OnChildLoaded(GameMessages::ChildLoaded& childLoaded);
void OnMessageBoxResponse(Entity* sender, int32_t button, const std::u16string& identifier, const std::u16string& userData);
void OnChoiceBoxResponse(Entity* sender, int32_t button, const std::u16string& buttonIdentifier, const std::u16string& identifier);

View File

@@ -95,7 +95,7 @@ void QueryToLdf(Leaderboard& leaderboard, const std::vector<ILeaderboard::Entry>
// Score:1
entry.push_back(new LDFData<int32_t>(u"Streak", leaderboardEntry.secondaryScore));
// Streak:1
entry.push_back(new LDFData<float>(u"HitPercentage", (leaderboardEntry.tertiaryScore / 100.0f)));
entry.push_back(new LDFData<float>(u"HitPercentage", leaderboardEntry.tertiaryScore));
// HitPercentage:3 between 0 and 1
break;
case Racing:
@@ -199,9 +199,9 @@ std::vector<ILeaderboard::Entry> FilterFriends(const std::vector<ILeaderboard::E
std::vector<ILeaderboard::Entry> friendsLeaderboard;
for (const auto& entry : leaderboard) {
const auto res = std::ranges::find_if(friendOfPlayer, [&entry, relatedPlayer](const FriendData& data) {
return entry.charId == data.friendID || entry.charId == relatedPlayer;
return entry.charId == data.friendID;
});
if (res != friendOfPlayer.cend()) {
if (res != friendOfPlayer.cend() || entry.charId == relatedPlayer) {
friendsLeaderboard.push_back(entry);
}
}

View File

@@ -7,7 +7,6 @@ set(DGAME_DCOMPONENTS_SOURCES
"BuildBorderComponent.cpp"
"CharacterComponent.cpp"
"CollectibleComponent.cpp"
"Component.cpp"
"ControllablePhysicsComponent.cpp"
"DestroyableComponent.cpp"
"DonationVendorComponent.cpp"

View File

@@ -1,34 +0,0 @@
#include "Component.h"
Component::Component(Entity* parent) {
m_Parent = parent;
}
Component::~Component() {
}
Entity* Component::GetParent() const {
return m_Parent;
}
void Component::Update(float deltaTime) {
}
void Component::OnUse(Entity* originator) {
}
void Component::UpdateXml(tinyxml2::XMLDocument& doc) {
}
void Component::LoadFromXml(const tinyxml2::XMLDocument& doc) {
}
void Component::Serialize(RakNet::BitStream& outBitStream, bool isConstruction) {
}

View File

@@ -1,6 +1,12 @@
#pragma once
#include "tinyxml2.h"
namespace tinyxml2 {
class XMLDocument;
}
namespace RakNet {
class BitStream;
}
class Entity;
@@ -9,40 +15,40 @@ class Entity;
*/
class Component {
public:
Component(Entity* parent);
virtual ~Component();
Component(Entity* parent) : m_Parent{ parent } {}
virtual ~Component() = default;
/**
* Gets the owner of this component
* @return the owner of this component
*/
Entity* GetParent() const;
Entity* GetParent() const { return m_Parent; }
/**
* Updates the component in the game loop
* @param deltaTime time passed since last update
*/
virtual void Update(float deltaTime);
virtual void Update(float deltaTime) {}
/**
* Event called when this component is being used, e.g. when some entity interacted with it
* @param originator
*/
virtual void OnUse(Entity* originator);
virtual void OnUse(Entity* originator) {}
/**
* Save data from this componennt to character XML
* @param doc the document to write data to
*/
virtual void UpdateXml(tinyxml2::XMLDocument& doc);
virtual void UpdateXml(tinyxml2::XMLDocument& doc) {}
/**
* Load base data for this component from character XML
* @param doc the document to read data from
*/
virtual void LoadFromXml(const tinyxml2::XMLDocument& doc);
virtual void LoadFromXml(const tinyxml2::XMLDocument& doc) {}
virtual void Serialize(RakNet::BitStream& outBitStream, bool isConstruction);
virtual void Serialize(RakNet::BitStream& outBitStream, bool isConstruction) {}
protected:

View File

@@ -31,6 +31,7 @@
#include "eStateChangeType.h"
#include "eUseItemResponse.h"
#include "Mail.h"
#include "ProximityMonitorComponent.h"
#include "CDComponentsRegistryTable.h"
#include "CDInventoryComponentTable.h"
@@ -68,9 +69,10 @@ InventoryComponent::InventoryComponent(Entity* parent) : Component(parent) {
auto slot = 0u;
for (const auto& item : items) {
if (!item.equip || !Inventory::IsValidItem(item.itemid)) {
continue;
}
if (!Inventory::IsValidItem(item.itemid)) continue;
AddItem(item.itemid, item.count);
if (!item.equip) continue;
const LWOOBJID id = ObjectIDManager::GenerateObjectID();
@@ -829,6 +831,30 @@ void InventoryComponent::EquipItem(Item* item, const bool skipChecks) {
break;
}
return;
} else if (item->GetLot() == 8092) {
// Trying to equip a car
const auto proximityObjects = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::PROXIMITY_MONITOR);
// look for car instancers and check if we are in its setup range
for (auto* const entity : proximityObjects) {
if (!entity) continue;
auto* proximityMonitorComponent = entity->GetComponent<ProximityMonitorComponent>();
if (!proximityMonitorComponent) continue;
if (proximityMonitorComponent->IsInProximity("Interaction_Distance", m_Parent->GetObjectID())) {
// in the range of a car instancer
entity->OnUse(m_Parent);
GameMessages::UseItemOnClient itemMsg;
itemMsg.target = entity->GetObjectID();
itemMsg.itemLOT = item->GetLot();
itemMsg.itemToUse = item->GetId();
itemMsg.playerId = m_Parent->GetObjectID();
itemMsg.Send(m_Parent->GetSystemAddress());
break;
}
}
return;
}

View File

@@ -265,6 +265,7 @@ void MovementAIComponent::PullToPoint(const NiPoint3& point) {
void MovementAIComponent::SetPath(std::vector<PathWaypoint> path) {
if (path.empty()) return;
while (!m_CurrentPath.empty()) m_CurrentPath.pop();
std::for_each(path.rbegin(), path.rend() - 1, [this](const PathWaypoint& point) {
this->m_CurrentPath.push(point);
});

View File

@@ -524,7 +524,7 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) {
GameMessages::SendRegisterPetDBID(m_Tamer, petSubKey, tamer->GetSystemAddress());
inventoryComponent->AddItem(m_Parent->GetLOT(), 1, eLootSourceType::ACTIVITY, eInventoryType::MODELS, {}, LWOOBJID_EMPTY, true, false, petSubKey);
inventoryComponent->AddItem(m_Parent->GetLOT(), 1, eLootSourceType::INVENTORY, eInventoryType::MODELS, {}, LWOOBJID_EMPTY, true, false, petSubKey);
auto* item = inventoryComponent->FindItemBySubKey(petSubKey, MODELS);
if (item == nullptr) {

View File

@@ -84,6 +84,10 @@ dpEntity* PhysicsComponent::CreatePhysicsEntity(eReplicaComponentType type) {
} else if (info->physicsAsset == "env\\env_won_fv_gas-blocking-volume.hkx") {
toReturn = new dpEntity(m_Parent->GetObjectID(), 390.496826f, 111.467964f, 600.821534f, true);
m_Position.y -= (111.467964f * m_Parent->GetDefaultScale()) / 2;
} else if (info->physicsAsset == "env\\GFTrack_DeathVolume1_CaveExit.hkx") {
toReturn = new dpEntity(m_Parent->GetObjectID(), 112.416870f, 50.363434f, 87.679268f);
} else if (info->physicsAsset == "env\\GFTrack_DeathVolume2_RoadGaps.hkx") {
toReturn = new dpEntity(m_Parent->GetObjectID(), 48.386536f, 50.363434f, 259.361755f);
} else {
// LOG_DEBUG("This one is supposed to have %s", info->physicsAsset.c_str());

View File

@@ -35,7 +35,8 @@
RacingControlComponent::RacingControlComponent(Entity* parent)
: Component(parent) {
m_PathName = u"MainPath";
m_RemainingLaps = 3;
m_NumberOfLaps = 3;
m_RemainingLaps = m_NumberOfLaps;
m_LeadingPlayer = LWOOBJID_EMPTY;
m_RaceBestTime = 0;
m_RaceBestLap = 0;
@@ -284,7 +285,7 @@ void RacingControlComponent::OnRacingClientReady(Entity* player) {
Game::entityManager->SerializeEntity(m_Parent);
}
void RacingControlComponent::OnRequestDie(Entity* player) {
void RacingControlComponent::OnRequestDie(Entity* player, const std::u16string& deathType) {
// Sent by the client when they collide with something which should smash
// them.
@@ -300,8 +301,9 @@ void RacingControlComponent::OnRequestDie(Entity* player) {
if (!racingPlayer.noSmashOnReload) {
racingPlayer.smashedTimes++;
LOG("Death type %s", GeneralUtils::UTF16ToWTF8(deathType).c_str());
GameMessages::SendDie(vehicle, vehicle->GetObjectID(), LWOOBJID_EMPTY, true,
eKillType::VIOLENT, u"", 0, 0, 90.0f, false, true, 0);
eKillType::VIOLENT, deathType, 0, 0, 90.0f, false, true, 0);
auto* destroyableComponent = vehicle->GetComponent<DestroyableComponent>();
uint32_t respawnImagination = 0;
@@ -658,23 +660,9 @@ void RacingControlComponent::Update(float deltaTime) {
}
}
// Spawn imagination pickups
auto* minSpawner = Game::zoneManager->GetSpawnersByName(
"ImaginationSpawn_Min")[0];
auto* medSpawner = Game::zoneManager->GetSpawnersByName(
"ImaginationSpawn_Med")[0];
auto* maxSpawner = Game::zoneManager->GetSpawnersByName(
"ImaginationSpawn_Max")[0];
minSpawner->Activate();
if (m_LoadedPlayers > 2) {
medSpawner->Activate();
}
if (m_LoadedPlayers > 4) {
maxSpawner->Activate();
}
GameMessages::ZoneLoadedInfo zoneLoadInfo{};
zoneLoadInfo.maxPlayers = m_LoadedPlayers;
m_Parent->GetScript()->OnZoneLoadedInfo(m_Parent, zoneLoadInfo);
// Reset players to their start location, without smashing them
for (auto& player : m_RacingPlayers) {
@@ -764,7 +752,7 @@ void RacingControlComponent::Update(float deltaTime) {
// new checkpoint
uint32_t respawnIndex = 0;
for (const auto& waypoint : path->pathWaypoints) {
if (player.lap == 3) {
if (player.lap == m_NumberOfLaps) {
break;
}
@@ -835,7 +823,7 @@ void RacingControlComponent::Update(float deltaTime) {
// Progress lap time tasks
missionComponent->Progress(eMissionTaskType::RACING, lapTime.count(), static_cast<LWOOBJID>(eRacingTaskParam::LAP_TIME));
if (player.lap == 3) {
if (player.lap == m_NumberOfLaps) {
m_Finished++;
player.finished = m_Finished;
@@ -882,3 +870,20 @@ void RacingControlComponent::Update(float deltaTime) {
}
}
}
void RacingControlComponent::MsgConfigureRacingControl(const GameMessages::ConfigureRacingControl& msg) {
for (const auto& dataUnique : msg.racingSettings) {
if (!dataUnique) continue;
const auto* const data = dataUnique.get();
if (data->GetKey() == u"Race_PathName" && data->GetValueType() == LDF_TYPE_UTF_16) {
m_PathName = static_cast<const LDFData<std::u16string>*>(data)->GetValue();
} else if (data->GetKey() == u"activityID" && data->GetValueType() == LDF_TYPE_S32) {
m_ActivityID = static_cast<const LDFData<int32_t>*>(data)->GetValue();
} else if (data->GetKey() == u"Number_of_Laps" && data->GetValueType() == LDF_TYPE_S32) {
m_NumberOfLaps = static_cast<const LDFData<int32_t>*>(data)->GetValue();
m_RemainingLaps = m_NumberOfLaps;
} else if (data->GetKey() == u"Minimum_Players_for_Group_Achievements" && data->GetValueType() == LDF_TYPE_S32) {
m_MinimumPlayersForGroupAchievements = static_cast<const LDFData<int32_t>*>(data)->GetValue();
}
}
}

View File

@@ -135,7 +135,7 @@ public:
/**
* Invoked when the client says it should be smashed.
*/
void OnRequestDie(Entity* player);
void OnRequestDie(Entity* player, const std::u16string& deathType = u"");
/**
* Invoked when the player has finished respawning.
@@ -152,6 +152,8 @@ public:
*/
RacingPlayerInfo* GetPlayerData(LWOOBJID playerID);
void MsgConfigureRacingControl(const GameMessages::ConfigureRacingControl& msg);
private:
/**
@@ -161,11 +163,13 @@ private:
/**
* The paths that are followed for the camera scenes
* Configurable in the ConfigureRacingControl msg with the key `Race_PathName`.
*/
std::u16string m_PathName;
/**
* The ID of the activity for participating in this race
* Configurable in the ConfigureRacingControl msg with the key `activityID`.
*/
uint32_t m_ActivityID;
@@ -245,5 +249,20 @@ private:
* Value for message box response to know if we are exiting the race via the activity dialogue
*/
const int32_t m_ActivityExitConfirm = 1;
bool m_AllPlayersReady = false;
/**
* @brief The number of laps in this race. Configurable in the ConfigureRacingControl msg
* with the key `Number_of_Laps`.
*
*/
int32_t m_NumberOfLaps{ 3 };
/**
* @brief The minimum number of players required to progress group achievements.
* Configurable with the ConfigureRacingControl msg with the key `Minimum_Players_for_Group_Achievements`.
*
*/
int32_t m_MinimumPlayersForGroupAchievements{ 2 };
};

View File

@@ -123,6 +123,11 @@ void SkillComponent::SyncPlayerProjectile(const LWOOBJID projectileId, RakNet::B
behavior->Handle(sync_entry.context, bitStream, branch);
this->m_managedProjectiles.erase(this->m_managedProjectiles.begin() + index);
GameMessages::ActivityNotify notify;
notify.notification.push_back( std::make_unique<LDFData<int32_t>>(u"shot_done", sync_entry.skillId));
m_Parent->OnActivityNotify(notify);
}
void SkillComponent::RegisterPlayerProjectile(const LWOOBJID projectileId, BehaviorContext* context, const BehaviorBranchContext& branch, const LOT lot) {
@@ -132,6 +137,7 @@ void SkillComponent::RegisterPlayerProjectile(const LWOOBJID projectileId, Behav
entry.branchContext = branch;
entry.lot = lot;
entry.id = projectileId;
entry.skillId = context->skillID;
this->m_managedProjectiles.push_back(entry);
}

View File

@@ -40,6 +40,8 @@ struct ProjectileSyncEntry {
BehaviorBranchContext branchContext{ 0, 0 };
int32_t skillId{ 0 };
explicit ProjectileSyncEntry();
};

View File

@@ -117,7 +117,6 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System
}
case MessageType::Game::PLAYER_LOADED: {
GameMessages::SendRestoreToPostLoadStats(entity, sysAddr);
entity->SetPlayerReadyForUpdates();
auto* ghostComponent = entity->GetComponent<GhostComponent>();
@@ -135,6 +134,8 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System
}
}
GameMessages::SendRestoreToPostLoadStats(entity, sysAddr);
auto* destroyable = entity->GetComponent<DestroyableComponent>();
destroyable->SetImagination(destroyable->GetImagination());
Game::entityManager->SerializeEntity(entity);
@@ -703,6 +704,12 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System
case MessageType::Game::UPDATE_INVENTORY_GROUP_CONTENTS:
GameMessages::HandleUpdateInventoryGroupContents(inStream, entity, sysAddr);
break;
case MessageType::Game::SHOOTING_GALLERY_FIRE: {
GameMessages::ShootingGalleryFire fire{};
fire.Deserialize(inStream);
fire.Handle(*entity, sysAddr);
break;
}
default:
LOG_DEBUG("Received Unknown GM with ID: %4i, %s", messageID, StringifiedEnum::ToString(messageID).data());

View File

@@ -6342,36 +6342,88 @@ void GameMessages::SendUpdateInventoryUi(LWOOBJID objectId, const SystemAddress&
SEND_PACKET;
}
void GameMessages::DisplayTooltip::Send() const {
CBITSTREAM;
CMSGHEADER;
namespace GameMessages {
void GameMsg::Send(const SystemAddress& sysAddr) const {
CBITSTREAM;
CMSGHEADER;
bitStream.Write(target);
bitStream.Write(msgId);
bitStream.Write(target); // Who this message will be sent to on the (a) client
bitStream.Write(msgId); // the ID of this message
bitStream.Write(doOrDie);
bitStream.Write(noRepeat);
bitStream.Write(noRevive);
bitStream.Write(isPropertyTooltip);
bitStream.Write(show);
bitStream.Write(translate);
bitStream.Write(time);
bitStream.Write<int32_t>(id.size());
bitStream.Write(id);
Serialize(bitStream); // write the message data
std::string toWrite;
for (const auto* item : localizeParams) {
toWrite += item->GetString() + "\n";
// Send to everyone if someone sent unassigned system address, or to one specific client.
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) {
SEND_PACKET_BROADCAST;
} else {
SEND_PACKET;
}
}
if (!toWrite.empty()) toWrite.pop_back();
bitStream.Write<int32_t>(toWrite.size());
bitStream.Write(GeneralUtils::ASCIIToUTF16(toWrite));
if (!toWrite.empty()) bitStream.Write<uint16_t>(0x00); // Null Terminator
bitStream.Write<int32_t>(imageName.size());
bitStream.Write(imageName);
bitStream.Write<int32_t>(text.size());
bitStream.Write(text);
void DisplayTooltip::Serialize(RakNet::BitStream& bitStream) const {
bitStream.Write(doOrDie);
bitStream.Write(noRepeat);
bitStream.Write(noRevive);
bitStream.Write(isPropertyTooltip);
bitStream.Write(show);
bitStream.Write(translate);
bitStream.Write(time);
bitStream.Write<int32_t>(id.size());
bitStream.Write(id);
SEND_PACKET;
std::string toWrite;
for (const auto* item : localizeParams) {
toWrite += item->GetString() + "\n";
}
if (!toWrite.empty()) toWrite.pop_back();
bitStream.Write<int32_t>(toWrite.size());
bitStream.Write(GeneralUtils::ASCIIToUTF16(toWrite));
if (!toWrite.empty()) bitStream.Write<uint16_t>(0x00); // Null Terminator
bitStream.Write<int32_t>(imageName.size());
bitStream.Write(imageName);
bitStream.Write<int32_t>(text.size());
bitStream.Write(text);
}
void UseItemOnClient::Serialize(RakNet::BitStream& bitStream) const {
bitStream.Write(itemLOT);
bitStream.Write(itemToUse);
bitStream.Write(itemType);
bitStream.Write(playerId);
bitStream.Write(targetPosition.x);
bitStream.Write(targetPosition.y);
bitStream.Write(targetPosition.z);
}
void SetModelToBuild::Serialize(RakNet::BitStream& bitStream) const {
bitStream.Write(modelLot != -1);
if (modelLot != -1) bitStream.Write(modelLot);
}
void SpawnModelBricks::Serialize(RakNet::BitStream& bitStream) const {
bitStream.Write(amount != 0.0f);
if (amount != 0.0f) bitStream.Write(amount);
bitStream.Write(position != NiPoint3Constant::ZERO);
if (position != NiPoint3Constant::ZERO) {
bitStream.Write(position.x);
bitStream.Write(position.y);
bitStream.Write(position.z);
}
}
bool ShootingGalleryFire::Deserialize(RakNet::BitStream& bitStream) {
if (!bitStream.Read(target.x)) return false;
if (!bitStream.Read(target.y)) return false;
if (!bitStream.Read(target.z)) return false;
if (!bitStream.Read(rotation.w)) return false;
if (!bitStream.Read(rotation.x)) return false;
if (!bitStream.Read(rotation.y)) return false;
if (!bitStream.Read(rotation.z)) return false;
return true;
}
void ShootingGalleryFire::Handle(Entity& entity, const SystemAddress& sysAddr) {
entity.OnShootingGalleryFire(*this);
}
}

View File

@@ -52,10 +52,12 @@ namespace GameMessages {
struct GameMsg {
GameMsg(MessageType::Game gmId) : msgId{ gmId } {}
virtual ~GameMsg() = default;
virtual void Send() const {}
void Send(const SystemAddress& sysAddr) const;
virtual void Serialize(RakNet::BitStream& bitStream) const {}
virtual bool Deserialize(RakNet::BitStream& bitStream) { return true; }
virtual void Handle(Entity& entity, const SystemAddress& sysAddr) {};
MessageType::Game msgId;
LWOOBJID target{ LWOOBJID_EMPTY };
SystemAddress sysAddr{ UNASSIGNED_SYSTEM_ADDRESS };
};
class PropertyDataMessage;
@@ -705,7 +707,63 @@ namespace GameMessages {
std::vector<LDFBaseData*> localizeParams{};
std::u16string imageName{};
std::u16string text{};
void Send() const override;
void Serialize(RakNet::BitStream& bitStream) const override;
};
struct UseItemOnClient : public GameMsg {
UseItemOnClient() : GameMsg(MessageType::Game::USE_ITEM_ON_CLIENT) {}
LWOOBJID playerId{};
LWOOBJID itemToUse{};
uint32_t itemType{};
LOT itemLOT{};
NiPoint3 targetPosition{};
void Serialize(RakNet::BitStream& bitStream) const override;
};
struct ZoneLoadedInfo : public GameMsg {
ZoneLoadedInfo() : GameMsg(MessageType::Game::ZONE_LOADED_INFO) {}
int32_t maxPlayers{};
};
struct ConfigureRacingControl : public GameMsg {
ConfigureRacingControl() : GameMsg(MessageType::Game::CONFIGURE_RACING_CONTROL) {}
std::vector<std::unique_ptr<LDFBaseData>> racingSettings{};
};
struct SetModelToBuild : public GameMsg {
SetModelToBuild() : GameMsg(MessageType::Game::SET_MODEL_TO_BUILD) {}
void Serialize(RakNet::BitStream& bitStream) const override;
LOT modelLot{ -1 };
};
struct SpawnModelBricks : public GameMsg {
SpawnModelBricks() : GameMsg(MessageType::Game::SPAWN_MODEL_BRICKS) {}
void Serialize(RakNet::BitStream& bitStream) const override;
float amount{ 0.0f };
NiPoint3 position{ NiPoint3Constant::ZERO };
};
struct ActivityNotify : public GameMsg {
ActivityNotify() : GameMsg(MessageType::Game::ACTIVITY_NOTIFY) {}
std::vector<std::unique_ptr<LDFBaseData>> notification{};
};
struct ShootingGalleryFire : public GameMsg {
ShootingGalleryFire() : GameMsg(MessageType::Game::SHOOTING_GALLERY_FIRE) {}
bool Deserialize(RakNet::BitStream& bitStream) override;
void Handle(Entity& entity, const SystemAddress& sysAddr) override;
NiPoint3 target{};
NiQuaternion rotation{};
};
struct ChildLoaded : public GameMsg {
ChildLoaded() : GameMsg(MessageType::Game::CHILD_LOADED) {}
LOT templateID{};
LWOOBJID childID{};
};
};

View File

@@ -84,6 +84,24 @@ int main(int argc, char** argv) {
Server::SetupLogger("MasterServer");
if (!Game::logger) return EXIT_FAILURE;
auto folders = { "navmeshes", "migrations", "vanity" };
for (const auto folder : folders) {
if (!std::filesystem::exists(BinaryPathFinder::GetBinaryDir() / folder)) {
std::string msg = "The (" +
std::string(folder) +
") folder was not copied to the binary directory. Please copy the (" +
std::string(folder) +
") folder from your download to the binary directory or re-run cmake.";
LOG("%s", msg.c_str());
// toss an error box up for windows users running the download
#ifdef DARKFLAME_PLATFORM_WIN32
MessageBoxA(nullptr, msg.c_str(), "Missing Folder", MB_OK | MB_ICONERROR);
#endif
return EXIT_FAILURE;
}
}
if (!dConfig::Exists("authconfig.ini")) LOG("Could not find authconfig.ini, using default settings");
if (!dConfig::Exists("chatconfig.ini")) LOG("Could not find chatconfig.ini, using default settings");
if (!dConfig::Exists("masterconfig.ini")) LOG("Could not find masterconfig.ini, using default settings");
@@ -126,6 +144,7 @@ int main(int argc, char** argv) {
MigrationRunner::RunMigrations();
const auto resServerPath = BinaryPathFinder::GetBinaryDir() / "resServer";
std::filesystem::create_directories(resServerPath);
const bool cdServerExists = std::filesystem::exists(resServerPath / "CDServer.sqlite");
const bool oldCDServerExists = std::filesystem::exists(Game::assetManager->GetResPath() / "CDServer.sqlite");
const bool fdbExists = std::filesystem::exists(Game::assetManager->GetResPath() / "cdclient.fdb");
@@ -176,7 +195,7 @@ int main(int argc, char** argv) {
}
// Run migrations should any need to be run.
MigrationRunner::RunSQLiteMigrations();
MigrationRunner::RunSQLiteMigrations();
//If the first command line argument is -a or --account then make the user
//input a username and password, with the password being hidden.

View File

@@ -4,68 +4,146 @@
#include "Game.h"
#include "BinaryPathFinder.h"
void StartChatServer() {
#ifdef _WIN32
#include <windows.h>
#include <handleapi.h>
#include <processthreadsapi.h>
namespace {
const auto startup = STARTUPINFOW{
.cb = sizeof(STARTUPINFOW),
.lpReserved = nullptr,
.lpDesktop = nullptr,
.lpTitle = nullptr,
.dwX = 0,
.dwY = 0,
.dwXSize = 0,
.dwYSize = 0,
.dwXCountChars = 0,
.dwYCountChars = 0,
.dwFillAttribute = 0,
.dwFlags = 0,
.wShowWindow = 0,
.cbReserved2 = 0,
.lpReserved2 = nullptr,
.hStdInput = INVALID_HANDLE_VALUE,
.hStdOutput = INVALID_HANDLE_VALUE,
.hStdError = INVALID_HANDLE_VALUE,
};
}
#else
#include <unistd.h>
#endif
uint32_t StartChatServer() {
if (Game::ShouldShutdown()) {
LOG("Currently shutting down. Chat will not be restarted.");
return;
return 0;
}
auto chat_path = BinaryPathFinder::GetBinaryDir() / "ChatServer";
#ifdef _WIN32
chat_path.replace_extension(".exe");
auto chat_startup = startup;
auto chat_info = PROCESS_INFORMATION{};
if (!CreateProcessW(chat_path.wstring().data(), chat_path.wstring().data(),
nullptr, nullptr, false, 0, nullptr, nullptr,
&chat_startup, &chat_info))
{
LOG("Failed to launch ChatServer");
return 0;
}
// get pid and close unused handles
auto chat_pid = chat_info.dwProcessId;
CloseHandle(chat_info.hProcess);
CloseHandle(chat_info.hThread);
#else // *nix systems
const auto chat_pid = fork();
if (chat_pid < 0) {
LOG("Failed to launch ChatServer");
return 0;
} else if (chat_pid == 0) {
// We are the child process
execl(chat_path.string().c_str(), chat_path.string().c_str(), nullptr);
}
#ifdef __APPLE__
//macOS doesn't need sudo to run on ports < 1024
auto result = system(((BinaryPathFinder::GetBinaryDir() / "ChatServer").string() + "&").c_str());
#elif _WIN32
auto result = system(("start /B " + (BinaryPathFinder::GetBinaryDir() / "ChatServer.exe").string()).c_str());
#else
if (std::atoi(Game::config->GetValue("use_sudo_chat").c_str())) {
auto result = system(("sudo " + (BinaryPathFinder::GetBinaryDir() / "ChatServer").string() + "&").c_str());
} else {
auto result = system(((BinaryPathFinder::GetBinaryDir() / "ChatServer").string() + "&").c_str());
}
#endif
LOG("ChatServer PID is %d", chat_pid);
return chat_pid;
}
void StartAuthServer() {
uint32_t StartAuthServer() {
if (Game::ShouldShutdown()) {
LOG("Currently shutting down. Auth will not be restarted.");
return;
return 0;
}
#ifdef __APPLE__
auto result = system(((BinaryPathFinder::GetBinaryDir() / "AuthServer").string() + "&").c_str());
#elif _WIN32
auto result = system(("start /B " + (BinaryPathFinder::GetBinaryDir() / "AuthServer.exe").string()).c_str());
#else
if (std::atoi(Game::config->GetValue("use_sudo_auth").c_str())) {
auto result = system(("sudo " + (BinaryPathFinder::GetBinaryDir() / "AuthServer").string() + "&").c_str());
} else {
auto result = system(((BinaryPathFinder::GetBinaryDir() / "AuthServer").string() + "&").c_str());
}
#endif
}
void StartWorldServer(LWOMAPID mapID, uint16_t port, LWOINSTANCEID lastInstanceID, int maxPlayers, LWOCLONEID cloneID) {
auto auth_path = BinaryPathFinder::GetBinaryDir() / "AuthServer";
#ifdef _WIN32
std::string cmd = "start /B " + (BinaryPathFinder::GetBinaryDir() / "WorldServer.exe").string() + " -zone ";
#else
std::string cmd;
if (std::atoi(Game::config->GetValue("use_sudo_world").c_str())) {
cmd = "sudo " + (BinaryPathFinder::GetBinaryDir() / "WorldServer").string() + " -zone ";
} else {
cmd = (BinaryPathFinder::GetBinaryDir() / "WorldServer").string() + " -zone ";
auth_path.replace_extension(".exe");
auto auth_startup = startup;
auto auth_info = PROCESS_INFORMATION{};
if (!CreateProcessW(auth_path.wstring().data(), auth_path.wstring().data(),
nullptr, nullptr, false, 0, nullptr, nullptr,
&auth_startup, &auth_info))
{
LOG("Failed to launch AuthServer");
return 0;
}
// get pid and close unused handles
auto auth_pid = auth_info.dwProcessId;
CloseHandle(auth_info.hProcess);
CloseHandle(auth_info.hThread);
#else // *nix systems
const auto auth_pid = fork();
if (auth_pid < 0) {
LOG("Failed to launch AuthServer");
return 0;
} else if (auth_pid == 0) {
// We are the child process
execl(auth_path.string().c_str(), auth_path.string().c_str(), nullptr);
}
#endif
cmd.append(std::to_string(mapID));
cmd.append(" -port ");
cmd.append(std::to_string(port));
cmd.append(" -instance ");
cmd.append(std::to_string(lastInstanceID));
cmd.append(" -maxclients ");
cmd.append(std::to_string(maxPlayers));
cmd.append(" -clone ");
cmd.append(std::to_string(cloneID));
#ifndef _WIN32
cmd.append("&"); //Sends our next process to the background on Linux
#endif
auto ret = system(cmd.c_str());
LOG("AuthServer PID is %d", auth_pid);
return auth_pid;
}
uint32_t StartWorldServer(LWOMAPID mapID, uint16_t port, LWOINSTANCEID lastInstanceID, int maxPlayers, LWOCLONEID cloneID) {
auto world_path = BinaryPathFinder::GetBinaryDir() / "WorldServer";
#ifdef _WIN32
world_path.replace_extension(".exe");
auto cmd = world_path.wstring() + L" -zone " + std::to_wstring(mapID) + L" -port " + std::to_wstring(port) +
L" -instance " + std::to_wstring(lastInstanceID) + L" -maxclients " + std::to_wstring(maxPlayers) +
L" -clone " + std::to_wstring(cloneID);
auto world_startup = startup;
auto world_info = PROCESS_INFORMATION{};
if (!CreateProcessW(world_path.wstring().data(), cmd.data(),
nullptr, nullptr, false, 0, nullptr, nullptr,
&world_startup, &world_info))
{
LOG("Failed to launch WorldServer");
return 0;
}
// get pid and close unused handles
auto world_pid = world_info.dwProcessId;
CloseHandle(world_info.hProcess);
CloseHandle(world_info.hThread);
#else
const auto world_pid = fork();
if (world_pid < 0) {
LOG("Failed to launch WorldServer");
return 0;
} else if (world_pid == 0) {
// We are the child process
execl(world_path.string().c_str(), world_path.string().c_str(),
"-zone", std::to_string(mapID).c_str(),
"-port", std::to_string(port).c_str(),
"-instance", std::to_string(lastInstanceID).c_str(),
"-maxclients", std::to_string(maxPlayers).c_str(),
"-clone", std::to_string(cloneID).c_str(), nullptr);
}
#endif
LOG("WorldServer PID is %d", world_pid);
return world_pid;
}

View File

@@ -1,6 +1,6 @@
#pragma once
#include "dCommonVars.h"
void StartAuthServer();
void StartChatServer();
void StartWorldServer(LWOMAPID mapID, uint16_t port, LWOINSTANCEID lastInstanceID, int maxPlayers, LWOCLONEID cloneID);
uint32_t StartAuthServer();
uint32_t StartChatServer();
uint32_t StartWorldServer(LWOMAPID mapID, uint16_t port, LWOINSTANCEID lastInstanceID, int maxPlayers, LWOCLONEID cloneID);

View File

@@ -10,6 +10,7 @@
#include "MessageType/Server.h"
#include "MessageType/Master.h"
#include "BinaryPathFinder.h"
#include "BitStreamUtils.h"
#include "MasterPackets.h"
#include "ZoneInstanceManager.h"
@@ -68,7 +69,16 @@ dServer::dServer(const std::string& ip, int port, int instanceID, int maxConnect
LOG("%s Server is listening on %s:%i with encryption: %i", StringifiedEnum::ToString(serverType).data(), ip.c_str(), port, int(useEncryption));
else
LOG("%s Server is listening on %s:%i with encryption: %i, running zone %i / %i", StringifiedEnum::ToString(serverType).data(), ip.c_str(), port, int(useEncryption), zoneID, instanceID);
} else { LOG("FAILED TO START SERVER ON IP/PORT: %s:%i", ip.c_str(), port); return; }
} else {
LOG("FAILED TO START SERVER ON IP/PORT: %s:%i", ip.c_str(), port);
#ifdef DARKFLAME_PLATFORM_LINUX
if (mServerType == ServerType::Auth) {
const auto cwd = BinaryPathFinder::GetBinaryDir();
LOG("Try running the following command before launching again:\n sudo setcap 'cap_net_bind_service=+ep' \"%s/AuthServer\"", cwd.string().c_str());
}
#endif
return;
}
mLogger->SetLogToConsole(prevLogSetting);
@@ -109,20 +119,23 @@ Packet* dServer::ReceiveFromMaster() {
if (packet) {
if (packet->length < 1) { mMasterPeer->DeallocatePacket(packet); return nullptr; }
if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) {
switch (packet->data[0]) {
case ID_DISCONNECTION_NOTIFICATION:
[[fallthrough]];
case ID_CONNECTION_LOST: {
LOG("Lost our connection to master, shutting DOWN!");
mMasterConnectionActive = false;
//ConnectToMaster(); //We'll just shut down now
// ConnectToMaster(); // We'll just shut down now
break;
}
if (packet->data[0] == ID_CONNECTION_REQUEST_ACCEPTED) {
case ID_CONNECTION_REQUEST_ACCEPTED: {
LOG("Established connection to master, zone (%i), instance (%i)", this->GetZoneID(), this->GetInstanceID());
mMasterConnectionActive = true;
mMasterSystemAddress = packet->systemAddress;
MasterPackets::SendServerInfo(this, packet);
break;
}
if (packet->data[0] == ID_USER_PACKET_ENUM) {
case ID_USER_PACKET_ENUM: {
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::MASTER) {
switch (static_cast<MessageType::Master>(packet->data[3])) {
case MessageType::Master::REQUEST_ZONE_TRANSFER_RESPONSE: {
@@ -133,12 +146,13 @@ Packet* dServer::ReceiveFromMaster() {
*mShouldShutdown = -2;
break;
//When we handle these packets in World instead dServer, we just return the packet's pointer.
// When we handle these packets in World instead dServer, we just return the packet's pointer.
default:
return packet;
}
}
break;
}
}
mMasterPeer->DeallocatePacket(packet);

View File

@@ -64,21 +64,22 @@ void AmSkullkinTower::SpawnLegs(Entity* self, const std::string& loc) {
info.rot = NiQuaternion::LookAt(info.pos, self->GetPosition());
auto* entity = Game::entityManager->CreateEntity(info);
auto* entity = Game::entityManager->CreateEntity(info, nullptr, self);
Game::entityManager->ConstructEntity(entity);
OnChildLoaded(self, entity);
}
void AmSkullkinTower::OnChildLoaded(Entity* self, Entity* child) {
auto legTable = self->GetVar<std::vector<LWOOBJID>>(u"legTable");
void AmSkullkinTower::OnChildLoaded(Entity& self, GameMessages::ChildLoaded& childLoaded) {
auto legTable = self.GetVar<std::vector<LWOOBJID>>(u"legTable");
legTable.push_back(child->GetObjectID());
legTable.push_back(childLoaded.childID);
self->SetVar(u"legTable", legTable);
self.SetVar(u"legTable", legTable);
const auto selfID = self->GetObjectID();
const auto selfID = self.GetObjectID();
auto* const child = Game::entityManager->GetEntity(childLoaded.childID);
if (!child) return;
child->AddDieCallback([this, selfID, child]() {
auto* self = Game::entityManager->GetEntity(selfID);

View File

@@ -8,7 +8,7 @@ public:
void SpawnLegs(Entity* self, const std::string& loc);
void OnChildLoaded(Entity* self, Entity* child);
void OnChildLoaded(Entity& self, GameMessages::ChildLoaded& childLoaded) override;
void NotifyDie(Entity* self, Entity* other, Entity* killer);

View File

@@ -77,8 +77,6 @@ void QbSpawner::OnTimerDone(Entity* self, std::string timerName) {
auto* child = Game::entityManager->CreateEntity(info, nullptr, self);
Game::entityManager->ConstructEntity(child);
OnChildLoaded(self, child);
} else {
auto* mob = Game::entityManager->GetEntity(mobTable[i]);
AggroTargetObject(self, mob);
@@ -88,16 +86,19 @@ void QbSpawner::OnTimerDone(Entity* self, std::string timerName) {
}
}
void QbSpawner::OnChildLoaded(Entity* self, Entity* child) {
auto mobTable = self->GetVar<std::vector<LWOOBJID>>(u"mobTable");
void QbSpawner::OnChildLoaded(Entity& self, GameMessages::ChildLoaded& childLoaded) {
auto* const child = Game::entityManager->GetEntity(childLoaded.childID);
if (!child) return;
auto mobTable = self.GetVar<std::vector<LWOOBJID>>(u"mobTable");
auto tableLoc = child->GetVar<int>(u"mobTableLoc");
mobTable[tableLoc] = child->GetObjectID();
self->SetVar<std::vector<LWOOBJID>>(u"mobTable", mobTable);
self.SetVar<std::vector<LWOOBJID>>(u"mobTable", mobTable);
AggroTargetObject(self, child);
AggroTargetObject(&self, child);
const auto selfID = self->GetObjectID();
const auto selfID = self.GetObjectID();
child->AddDieCallback([this, selfID, child]() {
auto* self = Game::entityManager->GetEntity(selfID);

View File

@@ -6,7 +6,7 @@ public:
void OnStartup(Entity* self) override;
void OnFireEventServerSide(Entity* self, Entity* sender, std::string args, int32_t param1, int32_t param2, int32_t param3) override;
void OnTimerDone(Entity* self, std::string timerName) override;
void OnChildLoaded(Entity* self, Entity* child);
void OnChildLoaded(Entity& self, GameMessages::ChildLoaded& childLoaded) override;
void OnChildRemoved(Entity* self, Entity* child);
void AggroTargetObject(Entity* self, Entity* enemy);
private:

View File

@@ -91,7 +91,7 @@ void NtCombatChallengeServer::SpawnTargetDummy(Entity* self) {
info.rot = self->GetRotation();
info.settings = { new LDFData<std::string>(u"custom_script_server", "scripts\\02_server\\Map\\NT\\L_NT_COMBAT_CHALLENGE_DUMMY.lua") };
auto* dummy = Game::entityManager->CreateEntity(info);
auto* dummy = Game::entityManager->CreateEntity(info, nullptr, self);
dummy->SetVar(u"challengeObjectID", self->GetObjectID());
@@ -104,26 +104,18 @@ void NtCombatChallengeServer::SetAttackImmunity(LWOOBJID objID, bool bTurnOn) {
}
void NtCombatChallengeServer::OnChildLoaded(Entity* self, Entity* child) {
auto targetNumber = self->GetVar<int32_t>(u"TargetNumber");
if (targetNumber == 0) targetNumber = 1;
self->SetVar(u"TargetNumber", targetNumber + 1);
void NtCombatChallengeServer::OnChildLoaded(Entity& self, GameMessages::ChildLoaded& childLoaded) {
auto* const child = Game::entityManager->GetEntity(childLoaded.childID);
const auto playerID = self->GetVar<LWOOBJID>(u"playerID");
auto* player = Game::entityManager->GetEntity(playerID);
if (player == nullptr) {
return;
if (child) {
child->SetRotation(NiQuaternion::FromEulerAngles(child->GetRotation().GetEulerAngles() += NiPoint3(0, PI, 0))); // rotate 180 degrees
}
child->SetRotation(NiQuaternion::LookAt(child->GetPosition(), player->GetPosition()));
self->SetVar(u"currentTargetID", child->GetObjectID());
self.SetVar(u"currentTargetID", child->GetObjectID());
Game::entityManager->SerializeEntity(child);
child->GetGroups().push_back("targets_" + std::to_string(self->GetObjectID()));
child->GetGroups().push_back("targets_" + std::to_string(self.GetObjectID()));
}
void NtCombatChallengeServer::ResetGame(Entity* self) {

View File

@@ -12,7 +12,7 @@ public:
void OnMessageBoxResponse(Entity* self, Entity* sender, int32_t button, const std::u16string& identifier, const std::u16string& userData) override;
void SpawnTargetDummy(Entity* self);
void SetAttackImmunity(LWOOBJID objID, bool bTurnOn);
void OnChildLoaded(Entity* self, Entity* child);
void OnChildLoaded(Entity& self, GameMessages::ChildLoaded& childLoaded) override;
void ResetGame(Entity* self);
void OnActivityTimerUpdate(Entity* self, float timeRemaining);
void OnTimerDone(Entity* self, std::string timerName) override;

View File

@@ -12,7 +12,7 @@ void VeBricksampleServer::OnUse(Entity* self, Entity* user) {
auto* inventoryComponent = user->GetComponent<InventoryComponent>();
if (loot && inventoryComponent != nullptr && inventoryComponent->GetLotCount(loot) == 0) {
inventoryComponent->AddItem(loot, 1, eLootSourceType::ACTIVITY);
inventoryComponent->AddItem(loot, 1, eLootSourceType::NONE);
for (auto* brickEntity : Game::entityManager->GetEntitiesInGroup("Bricks")) {
GameMessages::SendNotifyClientObject(brickEntity->GetObjectID(), u"Pickedup");

View File

@@ -10,7 +10,7 @@ void VeMissionConsole::OnUse(Entity* self, Entity* user) {
auto* inventoryComponent = user->GetComponent<InventoryComponent>();
if (inventoryComponent != nullptr) {
inventoryComponent->AddItem(12547, 1, eLootSourceType::ACTIVITY); // Add the panel required for pickup
inventoryComponent->AddItem(12547, 1, eLootSourceType::NONE); // Add the panel required for pickup
}
// The flag to set is 101<number>

View File

@@ -163,7 +163,7 @@ int32_t ActivityManager::GetGameID(Entity* self) const {
float_t ActivityManager::ActivityTimerGetRemainingTime(Entity* self, const std::string& timerName) const {
auto* timer = GetTimer(timerName);
return timer != nullptr ? std::min(timer->stopTime - timer->runTime, 0.0f) : 0.0f;
return timer != nullptr ? std::max(timer->stopTime - timer->runTime, 0.0f) : 0.0f;
}
void ActivityManager::ActivityTimerReset(Entity* self, const std::string& timerName) {

View File

@@ -294,6 +294,9 @@
#include "ShardArmor.h"
#include "TeslaPack.h"
#include "StunImmunity.h"
#include "GfRaceServer.h"
#include "FvRaceServer.h"
#include "VehicleDeathTriggerWaterServer.h"
// Survival scripts
#include "AgSurvivalStromling.h"
@@ -329,6 +332,8 @@
#include "WblRobotCitizen.h"
#include "EnemyClearThreat.h"
#include "AgSpiderBossMessage.h"
#include "GfRaceInstancer.h"
#include "NsRaceServer.h"
#include <map>
#include <string>
@@ -690,6 +695,11 @@ namespace {
{"scripts\\zone\\LUPs\\RobotCity Intro\\WBL_RCIntro_RobotCitizenYellow.lua", []() {return new WblRobotCitizen();}},
{"scripts\\02_server\\Map\\General\\L_ENEMY_CLEAR_THREAT.lua", []() {return new EnemyClearThreat();}},
{"scripts\\ai\\AG\\L_AG_SPIDER_BOSS_MESSAGE.lua", []() {return new AgSpiderBossMessage();}},
{"scripts\\ai\\GF\\L_GF_RACE_INSTANCER.lua", []() {return new GfRaceInstancer();}},
{"scripts\\ai\\RACING\\TRACK_NS\\NS_RACE_SERVER.lua", []() {return new NsRaceServer();}},
{"scripts\\ai\\RACING\\TRACK_GF\\GF_RACE_SERVER.lua", []() {return new GfRaceServer();}},
{"scripts\\ai\\RACING\\TRACK_FV\\FV_RACE_SERVER.lua", []() {return new FvRaceServer();}},
{"scripts\\ai\\RACING\\OBJECTS\\VEHICLE_DEATH_TRIGGER_WATER_SERVER.lua", []() {return new VehicleDeathTriggerWaterServer();}},
};
@@ -702,7 +712,12 @@ namespace {
"scripts\\empty.lua",
"scripts\\zone\\AG\\L_ZONE_AG.lua",
"scripts\\zone\\NS\\L_ZONE_NS.lua",
"scripts\\zone\\GF\\L_ZONE_GF.lua",
"scripts\\ai\\GF\\L_ZONE_GF.lua",
"scripts\\ai\\AG\\CONCERT_STAGE.lua",
"scripts\\ai\\NS\\L_NS_CAR_MODULAR_BUILD.lua", // In our implementation, this is done in GameMessages.cpp
"scripts\\ai\\PETS\\PET_BLOCKER.lua",
"scripts\\ai\\PETS\\PET_FLEA_MISSION.lua",
"scripts\\ai\\ACT\\L_ACT_PET_INSTANCE_EXIT.lua",
};
};

View File

@@ -354,7 +354,33 @@ namespace CppScripts {
* @param player the player to remove
* @param canceled if it was done via the cancel button
*/
virtual void OnRequestActivityExit(Entity* sender, LWOOBJID player, bool canceled){};
virtual void OnRequestActivityExit(Entity* sender, LWOOBJID player, bool canceled) {};
virtual void OnZoneLoadedInfo(Entity* self, const GameMessages::ZoneLoadedInfo& info) {};
/**
* @brief Handles notifying when activity data is done
*
* @param self
* @param notify The parameters of the notification
*/
virtual void OnActivityNotify(Entity* self, GameMessages::ActivityNotify& notify) {};
/**
* @brief handles shooting gallery fire
*
* @param self
* @param fire The firing data
*/
virtual void OnShootingGalleryFire(Entity& self, GameMessages::ShootingGalleryFire& fire) {};
/**
* @brief Handles when a child is loaded
*
* @param self
* @param fire The child info
*/
virtual void OnChildLoaded(Entity& self, GameMessages::ChildLoaded& childLoaded) {};
};
Script* const GetScript(Entity* parent, const std::string& scriptName);

View File

@@ -25,11 +25,10 @@ void AgSpiderBossMessage::MakeBox(Entity* self) const {
if (!tgt) return;
GameMessages::DisplayTooltip tooltip;
tooltip.target = tgt->GetObjectID();
tooltip.sysAddr = tgt->GetSystemAddress();
tooltip.show = true;
tooltip.text = box.boxText;
tooltip.time = box.boxTime * 1000; // to ms
tooltip.Send();
tooltip.Send(tgt->GetSystemAddress());
}
void AgSpiderBossMessage::OnCollisionPhantom(Entity* self, Entity* target) {

View File

@@ -1,4 +1,5 @@
set(DSCRIPTS_SOURCES_AI_GF
"GfRaceInstancer.cpp"
"GfCampfire.cpp"
"GfOrgan.cpp"
"GfBanana.cpp"

View File

@@ -0,0 +1,7 @@
#include "GfRaceInstancer.h"
#include "Entity.h"
void GfRaceInstancer::OnStartup(Entity* self) {
self->SetProximityRadius(self->HasVar(u"interaction_distance") ? self->GetVar<float>(u"interaction_distance") : 16.0f, "Interaction_Distance");
}

View File

@@ -0,0 +1,11 @@
#ifndef GFRACEINSTANCER_H
#define GFRACEINSTANCER_H
#include "CppScripts.h"
class GfRaceInstancer : public CppScripts::Script {
public:
void OnStartup(Entity* self) override;
};
#endif //!GFRACEINSTANCER_H

View File

@@ -16,6 +16,8 @@
#include "eReplicaComponentType.h"
#include "RenderComponent.h"
#include "eGameActivity.h"
#include "Item.h"
#include <ranges>
void SGCannon::OnStartup(Entity* self) {
LOG("OnStartup");
@@ -81,10 +83,6 @@ void SGCannon::OnActivityStateChangeRequest(Entity* self, LWOOBJID senderID, int
auto* player = Game::entityManager->GetEntity(self->GetVar<LWOOBJID>(PlayerIDVariable));
if (player != nullptr) {
LOG("Player is ready");
/*GameMessages::SendSetStunned(player->GetObjectID(), eStateChangeType::PUSH, player->GetSystemAddress(), LWOOBJID_EMPTY,
true, true, true, true, true, true, true);*/
LOG("Sending ActivityEnter");
GameMessages::SendActivityEnter(self->GetObjectID(), player->GetSystemAddress());
@@ -103,7 +101,6 @@ void SGCannon::OnActivityStateChangeRequest(Entity* self, LWOOBJID senderID, int
auto* characterComponent = player->GetComponent<CharacterComponent>();
if (characterComponent != nullptr) {
characterComponent->SetIsRacing(true);
characterComponent->SetCurrentActivity(eGameActivity::SHOOTING_GALLERY);
auto possessor = player->GetComponent<PossessorComponent>();
if (possessor) {
@@ -114,20 +111,12 @@ void SGCannon::OnActivityStateChangeRequest(Entity* self, LWOOBJID senderID, int
Game::entityManager->SerializeEntity(player);
}
self->SetNetworkVar<bool>(HideScoreBoardVariable, true);
self->SetNetworkVar<bool>(ReSetSuperChargeVariable, true);
self->SetNetworkVar<bool>(ShowLoadingUI, true);
self->AddCallbackTimer(1.0f, [self, this]() {
self->SetNetworkVar<bool>(HideScoreBoardVariable, true);
self->SetNetworkVar<bool>(ReSetSuperChargeVariable, true);
self->SetNetworkVar<bool>(ShowLoadingUI, true);
});
/*
GameMessages::SendTeleport(
player->GetObjectID(),
{-292.6415710449219, 230.20237731933594, -3.9090466499328613},
{0.7067984342575073, -6.527870573336259e-05, 0.707414984703064, 0.00021762956748716533},
player->GetSystemAddress(), true
);
*/
//GameMessages::SendRequestActivityEnter(self->GetObjectID(), player->GetSystemAddress(), false, player->GetObjectID());
} else {
LOG("Player not found");
}
@@ -245,14 +234,6 @@ void SGCannon::GameOverTimerFunc(Entity* self) {
GameMessages::SendActivityPause(self->GetObjectID(), true, player->GetSystemAddress());
/*const auto leftoverCannonballs = Game::entityManager->GetEntitiesInGroup("cannonball");
if (leftoverCannonballs.empty()) {
RecordPlayerScore(self);
} else {
ActivityTimerStart(self, EndGameBufferTimer, 1, leftoverCannonballs.size());
}*/
ActivityTimerStart(self, EndGameBufferTimer, 1, 1);
TimerToggle(self);
@@ -261,60 +242,51 @@ void SGCannon::GameOverTimerFunc(Entity* self) {
void SGCannon::DoSpawnTimerFunc(Entity* self, const std::string& name) {
if (self->GetVar<bool>(GameStartedVariable)) {
LOG_DEBUG("time name %s %s", name.c_str(), name.substr(7).c_str());
const auto spawnNumber = static_cast<uint32_t>(std::stoi(name.substr(7)));
const auto& activeSpawns = self->GetVar<std::vector<SGEnemy>>(ActiveSpawnsVariable);
LOG_DEBUG("size %i, %i", activeSpawns.size(), spawnNumber);
if (activeSpawns.size() <= spawnNumber) {
LOG_DEBUG("Trying to spawn %i when spawns size is only %i", spawnNumber, activeSpawns.size());
return;
}
const auto& toSpawn = activeSpawns.at(spawnNumber);
LOG_DEBUG("toSpawn %i", toSpawn.spawnPaths.size());
const auto pathIndex = GeneralUtils::GenerateRandomNumber<float_t>(0, toSpawn.spawnPaths.size() - 1);
LOG_DEBUG("index %f", pathIndex);
LOG_DEBUG("%s", toSpawn.spawnPaths.at(pathIndex).c_str());
const auto pathIndex = GeneralUtils::GenerateRandomNumber<size_t>(0, toSpawn.spawnPaths.size() - 1);
const auto* path = Game::zoneManager->GetZone()->GetPath(toSpawn.spawnPaths.at(pathIndex));
if (!path) {
LOG_DEBUG("Path %s at index %i is null", toSpawn.spawnPaths.at(pathIndex).c_str(), pathIndex);
if (!path || path->pathWaypoints.empty()) {
LOG_DEBUG("Path %s at index %i or has 0 waypoints", toSpawn.spawnPaths.at(pathIndex).c_str(), pathIndex);
return;
}
LOG_DEBUG("%s", path->pathName.c_str());
auto info = EntityInfo{};
info.lot = toSpawn.lot;
info.spawnerID = self->GetObjectID();
info.pos = path->pathWaypoints.at(0).position;
info.pos = path->pathWaypoints[0].position;
info.settings = {
new LDFData<SGEnemy>(u"SpawnData", toSpawn),
new LDFData<std::string>(u"custom_script_server", "scripts/ai/ACT/SG_TARGET.lua"),
new LDFData<std::string>(u"custom_script_server", "scripts/ai/ACT/SG_TARGET.lua"), // this script is never loaded
new LDFData<std::string>(u"custom_script_client", "scripts/client/ai/SG_TARGET_CLIENT.lua"),
new LDFData<std::string>(u"attached_path", path->pathName),
new LDFData<uint32_t>(u"attached_path_start", 0),
new LDFData<std::u16string>(u"groupID", u"SGEnemy")
new LDFData<std::u16string>(u"groupID", u"SGEnemy"),
new LDFData<uint32_t>(u"wave", self->GetVar<uint32_t>(ThisWaveVariable)),
};
LOG_DEBUG("Spawning enemy %i on path %s", toSpawn.lot, path->pathName.c_str());
auto* enemy = Game::entityManager->CreateEntity(info, nullptr, self);
Game::entityManager->ConstructEntity(enemy);
auto* movementAI = enemy->AddComponent<MovementAIComponent>(MovementAIInfo{});
auto* simplePhysicsComponent = enemy->GetComponent<SimplePhysicsComponent>();
if (simplePhysicsComponent) {
simplePhysicsComponent->SetPhysicsMotionState(4);
}
Game::entityManager->ConstructEntity(enemy);
movementAI->SetMaxSpeed(toSpawn.initialSpeed);
movementAI->SetCurrentSpeed(toSpawn.initialSpeed);
movementAI->SetHaltDistance(0.0f);
std::vector<PathWaypoint> pathWaypoints = path->pathWaypoints;
if (GeneralUtils::GenerateRandomNumber<float_t>(0, 1) < 0.5f) {
std::reverse(pathWaypoints.begin(), pathWaypoints.end());
}
movementAI->SetPath(pathWaypoints);
movementAI->SetPath(path->pathWaypoints);
enemy->AddDieCallback([this, self, enemy, name]() {
RegisterHit(self, enemy, name);
@@ -362,7 +334,10 @@ void SGCannon::StartGame(Entity* self) {
auto rewardObjects = Game::entityManager->GetEntitiesInGroup(constants.rewardModelGroup);
for (auto* reward : rewardObjects) {
reward->OnFireEventServerSide(self, ModelToBuildEvent);
GameMessages::SetModelToBuild modelToBuild{};
modelToBuild.modelLot = LOT_NULL;
modelToBuild.target = reward->GetObjectID();
modelToBuild.Send(UNASSIGNED_SYSTEM_ADDRESS);
}
auto* player = Game::entityManager->GetEntity(self->GetVar<LWOOBJID>(PlayerIDVariable));
@@ -400,6 +375,10 @@ void SGCannon::DoGameStartup(Entity* self) {
constants.firstWaveStartTime);
}
void SGCannon::OnShootingGalleryFire(Entity& self, GameMessages::ShootingGalleryFire& fire) {
self.SetVar<uint32_t>(ShotsFiredVariable, self.GetVar<uint32_t>(ShotsFiredVariable) + 1);
}
void SGCannon::SpawnNewModel(Entity* self) {
// Add a new reward to the existing rewards
@@ -407,6 +386,7 @@ void SGCannon::SpawnNewModel(Entity* self) {
if (currentReward != -1) {
auto rewards = self->GetVar<std::vector<LOT>>(RewardsVariable);
rewards.push_back(currentReward);
self->SetVar<std::vector<LOT>>(RewardsVariable, rewards);
self->SetNetworkVar<int32_t>(RewardAddedVariable, currentReward);
}
@@ -438,9 +418,13 @@ void SGCannon::SpawnNewModel(Entity* self) {
std::unordered_map<LOT, int32_t> toDrop = {};
toDrop = Loot::RollLootMatrix(player, lootMatrix);
for (auto drop : toDrop) {
rewardModel->OnFireEventServerSide(self, ModelToBuildEvent, drop.first);
self->SetVar<LOT>(CurrentRewardVariable, drop.first);
for (const auto [lot, count] : toDrop) {
GameMessages::SetModelToBuild modelToBuild{};
modelToBuild.modelLot = lot;
modelToBuild.target = rewardModel->GetObjectID();
modelToBuild.Send(player->GetSystemAddress());
self->SetVar<LOT>(CurrentRewardVariable, lot);
}
}
}
@@ -514,11 +498,11 @@ void SGCannon::RecordPlayerScore(Entity* self) {
const auto currentWave = self->GetVar<uint32_t>(ThisWaveVariable);
if (currentWave > 0) {
auto totalWaveScore = 0;
auto totalWaveScore = totalScore;
auto playerScores = self->GetVar<std::vector<int32_t>>(PlayerScoresVariable);
for (const auto& waveScore : playerScores) {
totalWaveScore += waveScore;
totalWaveScore -= waveScore;
}
if (currentWave >= playerScores.size()) {
@@ -526,6 +510,7 @@ void SGCannon::RecordPlayerScore(Entity* self) {
} else {
playerScores[currentWave] = totalWaveScore;
}
self->SetVar<std::vector<int32_t>>(PlayerScoresVariable, playerScores);
}
}
@@ -547,12 +532,11 @@ void SGCannon::PlaySceneAnimation(Entity* self, const std::u16string& animationN
}
void SGCannon::PauseChargeCannon(Entity* self) {
const auto time = std::max(static_cast<uint32_t>(std::ceil(ActivityTimerGetCurrentTime(self, SuperChargeTimer))), static_cast<uint32_t>(1));
const auto time = std::max(static_cast<uint32_t>(std::ceil(ActivityTimerGetRemainingTime(self, SuperChargeTimer))), static_cast<uint32_t>(1));
self->SetVar<bool>(SuperChargePausedVariable, true);
self->SetVar<uint32_t>(CurrentSuperChargedTimeVariable, time);
self->SetNetworkVar<uint32_t>(ChargeCountingVariable, time);
ActivityTimerStop(self, SuperChargeTimer);
}
@@ -568,14 +552,17 @@ void SGCannon::StopGame(Entity* self, bool cancel) {
// The player won, store all the score and send rewards
if (!cancel) {
int32_t percentage = 0.0f;
auto misses = self->GetVar<uint32_t>(MissesVariable);
auto fired = self->GetVar<uint32_t>(ShotsFiredVariable);
float percentage = 0.0f;
float misses = self->GetVar<uint32_t>(MissesVariable);
float fired = self->GetVar<uint32_t>(ShotsFiredVariable);
if (fired > 0) {
if (fired > 0.0f) {
percentage = misses / fired;
}
percentage = 1.0f - percentage;
percentage = std::max(percentage, 0.0f);
auto* missionComponent = player->GetComponent<MissionComponent>();
if (missionComponent != nullptr) {
@@ -596,13 +583,27 @@ void SGCannon::StopGame(Entity* self, bool cancel) {
auto* inventory = player->GetComponent<InventoryComponent>();
if (inventory != nullptr) {
for (const auto rewardLot : self->GetVar<std::vector<LOT>>(RewardsVariable)) {
inventory->AddItem(rewardLot, 1, eLootSourceType::ACTIVITY, eInventoryType::MODELS);
inventory->AddItem(rewardLot, 1, eLootSourceType::NONE, eInventoryType::MODELS);
}
}
self->SetNetworkVar<std::u16string>(u"UI_Rewards",
GeneralUtils::to_u16string(self->GetVar<int32_t>(TotalScoreVariable)) + u"_0_0_0_0_0_0"
);
const auto& waveScores = self->GetVar<std::vector<int32_t>>(PlayerScoresVariable);
std::stringstream stream;
stream << self->GetVar<int32_t>(TotalScoreVariable) << "_";
// technically unused in shooting gallery but serialize it regardless.
for (const auto& score : waveScores) {
stream << score << "_";
}
auto totalmissed = fired - misses;
if (totalmissed < 0) {
totalmissed = 0;
}
stream << fired << "_" << totalmissed << "_" << self->GetVar<uint32_t>(MaxStreakVariable);
self->SetNetworkVar<std::u16string>(u"UI_Rewards", GeneralUtils::ASCIIToUTF16(stream.str()));
}
GameMessages::SendActivityStop(self->GetObjectID(), false, cancel, player->GetSystemAddress());
@@ -617,10 +618,42 @@ void SGCannon::StopGame(Entity* self, bool cancel) {
ResetVars(self);
}
void SGCannon::OnActivityNotify(Entity* self, GameMessages::ActivityNotify& notify) {
if (!self->GetVar<bool>(GameStartedVariable)) return;
const auto& params = notify.notification;
if (params.empty()) return;
const auto& param = params[0];
if (param->GetValueType() != LDF_TYPE_S32 || param->GetKey() != u"shot_done") return;
const auto superChargeShotDone = static_cast<LDFData<int32_t>*>(param.get())->GetValue() == GetConstants().cannonSuperChargeSkill;
const auto& hitTargets = self->GetVar<std::vector<LWOOBJID>>(u"CannonBallKills");
if (hitTargets.empty() && !superChargeShotDone) {
self->SetVar<uint32_t>(u"m_curStreak", 0);
self->SetVar<uint32_t>(MissesVariable, self->GetVar<uint32_t>(MissesVariable) + 1);
self->SetNetworkVar<bool>(u"HideStreak", true);
self->SetNetworkVar<bool>(u"UnMarkAll", true);
UpdateStreak(self);
} else if (hitTargets.size() > 1) {
self->SetNetworkVar<bool>(u"mHit", true);
}
self->SetVar<std::vector<LWOOBJID>>(u"CannonBallKills", {});
}
void SGCannon::RegisterHit(Entity* self, Entity* target, const std::string& timerName) {
if (!self->GetVar<bool>(GameStartedVariable)) return;
auto cannonBallKills = self->GetVar<std::vector<LWOOBJID>>(u"CannonBallKills");
cannonBallKills.push_back(target->GetObjectID());
self->SetVar<std::vector<LWOOBJID>>(u"CannonBallKills", cannonBallKills);
const auto& spawnInfo = target->GetVar<SGEnemy>(u"SpawnData");
if (spawnInfo.respawns) {
if (spawnInfo.respawns && target->GetVar<uint32_t>(u"wave") == self->GetVar<uint32_t>(ThisWaveVariable)) {
const auto respawnTime = GeneralUtils::GenerateRandomNumber<float_t>(spawnInfo.minRespawnTime, spawnInfo.maxRespawnTime);
ActivityTimerStart(self, timerName, respawnTime, respawnTime);
@@ -637,6 +670,7 @@ void SGCannon::RegisterHit(Entity* self, Entity* target, const std::string& time
} else {
if (!self->GetVar<bool>(SuperChargeActiveVariable)) {
self->SetVar<uint32_t>(u"m_curStreak", 0);
self->SetVar<uint32_t>(MissesVariable, self->GetVar<uint32_t>(MissesVariable) + 1);
}
self->SetNetworkVar<bool>(u"hitFriend", true);
@@ -646,10 +680,6 @@ void SGCannon::RegisterHit(Entity* self, Entity* target, const std::string& time
auto scScore = self->GetVar<int32_t>(TotalScoreVariable) - lastSuperTotal;
LOG("LastSuperTotal: %i, scScore: %i, constants.chargedPoints: %i",
lastSuperTotal, scScore, constants.chargedPoints
);
if (!self->GetVar<bool>(SuperChargeActiveVariable) && scScore >= constants.chargedPoints && score >= 0) {
StartChargedCannon(self);
self->SetNetworkVar<float>(u"SuperChargeBar", 100.0f);
@@ -684,6 +714,40 @@ void SGCannon::RegisterHit(Entity* self, Entity* target, const std::string& time
if (missionComponent == nullptr) return;
missionComponent->Progress(eMissionTaskType::SMASH, spawnInfo.lot, self->GetObjectID());
auto matrix = self->GetVar<uint32_t>(MatrixVariable);
float rewardS = 0.0f;
float rewardF = 0.0f;
if (matrix <= 5) {
const auto scoreRewardNum = "Score_Reward_" + std::to_string(matrix);
const auto rewardAmountItr = constants.scoreRewards.find(scoreRewardNum);
if (rewardAmountItr != constants.scoreRewards.end()) {
const float rewardAmount = rewardAmountItr->second / 100 * 3;
rewardS = newScore / rewardAmount;
rewardF = std::round(rewardS * 3);
if (rewardF > 100.0f) rewardF = 100.0f;
self->SetNetworkVar(ModelPercentVariable, rewardF);
}
}
if (rewardF > 0.0f && rewardF < 200.0f && matrix <= 5) {
const auto rewardModelGroup = Game::entityManager->GetEntitiesInGroup(constants.rewardModelGroup);
if (!rewardModelGroup.empty()) {
auto* rewardModel = rewardModelGroup[0];
GameMessages::SpawnModelBricks spawnBricks{};
spawnBricks.target = rewardModel->GetObjectID();
spawnBricks.amount = rewardF / 100.0f;
spawnBricks.position = target->GetPosition();
spawnBricks.Send(player->GetSystemAddress());
if (rewardF >= 100.0f) {
SpawnNewModel(self);
self->SetVar<uint32_t>(MatrixVariable, matrix + 1);
}
}
}
}
void SGCannon::UpdateStreak(Entity* self) {
@@ -745,41 +809,34 @@ void SGCannon::ToggleSuperCharge(Entity* self, bool enable) {
auto* selfInventoryComponent = self->GetComponent<InventoryComponent>();
if (inventoryComponent == nullptr) {
LOG("Inventory component not found");
// This is a gm in the original script
Item* meItem1{};
Item* meItem2{};
for (const auto item : selfInventoryComponent->GetInventory(eInventoryType::ITEMS)->GetItems() | std::views::values) {
if (item->GetSlot() == 0) meItem1 = item;
else if (item->GetSlot() == 1) meItem2 = item;
}
if (!meItem1 || !meItem2) {
LOG("Cannon does not have the required items equipped");
return;
}
if (enable) {
LOG("Player is activating super charge");
selfInventoryComponent->UpdateSlot("greeble_r", { ObjectIDManager::GenerateRandomObjectID(), 6505, 1, 0 });
selfInventoryComponent->UpdateSlot("greeble_l", { ObjectIDManager::GenerateRandomObjectID(), 6506, 1, 0 });
selfInventoryComponent->EquipItem(meItem1);
selfInventoryComponent->EquipItem(meItem2);
// TODO: Equip items
skillID = constants.cannonSuperChargeSkill;
cooldown = 400;
} else {
selfInventoryComponent->UpdateSlot("greeble_r", { ObjectIDManager::GenerateRandomObjectID(), 0, 0, 0 });
selfInventoryComponent->UpdateSlot("greeble_l", { ObjectIDManager::GenerateRandomObjectID(), 0, 0, 0 });
selfInventoryComponent->UnEquipItem(meItem1);
selfInventoryComponent->UnEquipItem(meItem2);
self->SetNetworkVar<float>(u"SuperChargeBar", 0);
LOG("Player disables super charge");
// TODO: Unequip items
for (const auto& equipped : equippedItems) {
if (equipped.first == "special_r" || equipped.first == "special_l") {
LOG("Trying to unequip a weapon, %i", equipped.second.lot);
auto* item = inventoryComponent->FindItemById(equipped.second.id);
if (item != nullptr) {
inventoryComponent->UnEquipItem(item);
} else {
LOG("Item not found, %i", equipped.second.lot);
}
}
}
cooldown = 800;
self->SetVar<uint32_t>(NumberOfChargesVariable, 0);
}
@@ -794,10 +851,10 @@ void SGCannon::ToggleSuperCharge(Entity* self, bool enable) {
DynamicShootingGalleryParams properties = shootingGalleryComponent->GetDynamicParams();
properties.cannonFOV = 58.6f;
properties.cannonVelocity = 129.0;
properties.cannonFOV = constants.cannonFOV;
properties.cannonVelocity = constants.cannonVelocity;
properties.cannonRefireRate = cooldown;
properties.cannonMinDistance = 30;
properties.cannonMinDistance = constants.cannonMinDistance;
properties.cannonTimeout = -1;
shootingGalleryComponent->SetDynamicParams(properties);
@@ -829,22 +886,38 @@ std::vector<std::vector<SGEnemy>> SGCannon::GetWaves() {
1.0, false, true
},
// Sub 1
// Sub 1 but for dlu
{
std::vector<std::string> { "Wave_1_Sub_1", "Wave_1_Sub_2" },
6016, 0.0, 2.0, true, 0.0, 2.0,
10.0, 1000, false, 0.0, 1.0,
1.0, true, true
},
// Sub 2
{
std::vector<std::string> { "Wave_2_Sub_1", "Wave_2_Sub_2" },
6016, 0.0, 2.0, true, 0.0, 2.0,
2.0, 1000, false, 0.0, 1.0,
1.0, true, true
},
//// Sub 1
//{
// std::vector<std::string> { "Wave_1_Sub_1", "Wave_1_Sub_2" },
// 6016, 0.0, 2.0, true, 0.0, 2.0,
// 10.0, 1000, false, 0.0, 1.0,
// 1.0, true, true
//},
// Sub 2 but for dlu
{
std::vector<std::string> { "Wave_2_Sub_1", "Wave_2_Sub_2" },
6016, 0.0, 2.0, true, 0.0, 2.0,
3.0, 1000, false, 0.0, 1.0,
1.0, true, true
},
// Sub 2
//{
// std::vector<std::string> { "Wave_2_Sub_1", "Wave_2_Sub_2" },
// 6016, 0.0, 2.0, true, 0.0, 2.0,
// 2.0, 1000, false, 0.0, 1.0,
// 1.0, true, true
//},
// Friendly
{
std::vector<std::string> { "Wave_3_FShip_1", "Wave_3_FShip_2" },
@@ -897,10 +970,18 @@ std::vector<std::vector<SGEnemy>> SGCannon::GetWaves() {
},
// Sub 2
//{
// std::vector<std::string> { "Wave_2_Sub_1", "Wave_2_Sub_2" },
// 6016, 0.0, 2.0, true, 0.0, 2.0,
// 2.0, 1000, false, 0.0, 1.0,
// 1.0, true, true
//},
// Sub 2 but for dlu
{
std::vector<std::string> { "Wave_2_Sub_1", "Wave_2_Sub_2" },
6016, 0.0, 2.0, true, 0.0, 2.0,
2.0, 1000, false, 0.0, 1.0,
3.0, 1000, false, 0.0, 1.0,
1.0, true, true
},
@@ -963,11 +1044,19 @@ std::vector<std::vector<SGEnemy>> SGCannon::GetWaves() {
1.0, true, true
},
// Sub 2
//{
// std::vector<std::string> { "Wave_2_Sub_1", "Wave_2_Sub_2" },
// 6016, 0.0, 2.0, true, 0.0, 2.0,
// 2.0, 1000, false, 0.0, 1.0,
// 1.0, true, true
//},
// Sub 2
{
std::vector<std::string> { "Wave_2_Sub_1", "Wave_2_Sub_2" },
6016, 0.0, 2.0, true, 0.0, 2.0,
2.0, 1000, false, 0.0, 1.0,
3.0, 1000, false, 0.0, 1.0,
1.0, true, true
},
@@ -987,14 +1076,22 @@ std::vector<std::vector<SGEnemy>> SGCannon::GetWaves() {
1.0, false, true
},
// Ness
// Ness temp fix for dlu where speeds are set to 7 to match a speed closer to live while we work on movingplatform components.
{
std::vector<std::string> { "Wave_1_Ness_1", "Wave_1_Ness_2", "Wave_2_Ness_1" },
2565, 10.0, 15.0, true, 10.0, 15.0,
2.0, 10000, false, 0.0, 1.0,
1.0, true, true
7.0, 10000, false, 0.0, 7.0,
7.0, true, true
},
// // Ness
// {
// std::vector<std::string> { "Wave_1_Ness_1", "Wave_1_Ness_2", "Wave_2_Ness_1" },
// 2565, 10.0, 15.0, true, 10.0, 15.0,
// 2.0, 10000, false, 0.0, 1.0,
// 1.0, true, true
// },
// Friendly 1
{
std::vector<std::string> { "Wave_3_FShip_1", "Wave_3_FShip_2" },
@@ -1033,11 +1130,8 @@ void SGCannon::ResetVars(Entity* self) {
self->SetVar<int32_t>(TotalScoreVariable, 0);
self->SetVar<uint32_t>(u"m_curStreak", 0);
self->SetNetworkVar<float>(u"SuperChargeBar", 0);
self->SetVar<uint32_t>(u"LastSuperTotal", 0);
self->SetNetworkVar<float>(u"SuperChargeBar", 0.0f);
self->SetNetworkVar<bool>(u"ShowStreak", 0);
self->SetNetworkVar<bool>(u"UnMarkAll", true);
self->SetVar<std::vector<LWOOBJID>>(u"LastHitTarget", {});
const_cast<std::vector<SGEnemy>&>(self->GetVar<std::vector<SGEnemy>>(ActiveSpawnsVariable)).clear();
self->SetVar<std::vector<SGEnemy>>(ActiveSpawnsVariable, {});
@@ -1056,40 +1150,42 @@ void SGCannon::ResetVars(Entity* self) {
SGConstants SGCannon::GetConstants() {
return {
Vector3 { -908.542480, 229.773178, -908.542480 },
Quaternion { 0.91913521289825, 0, 0.39394217729568, 0 },
1864,
34,
1822,
Vector3 { 6.652, -2, 1.5 },
157,
129.0,
30.0,
800.0,
Vector3 { 0, 4.3, 9 },
6297,
1822,
249,
228,
-1,
58.6,
true,
2,
10,
25000,
"QBRewardGroup",
1864,
50000,
157,
100000,
187,
200000,
188,
400000,
189,
800000,
190,
4.0,
7.0
.playerStartPosition = Vector3 { -908.542480, 229.773178, -908.542480 },
.playerStartRotation = Quaternion { 0.91913521289825, 0, 0.39394217729568, 0 },
.cannonLot = 1864,
.impactSkillID = 34,
.projectileLot = 1822,
.playerOffset = Vector3 { 6.652, -2, 1.5 },
.rewardModelMatrix = 157,
.cannonVelocity = 129.0,
.cannonMinDistance = 30.0,
.cannonRefireRate = 800.0,
.cannonBarrelOffset = Vector3 { 0, 4.3, 9 },
.cannonSuperchargedProjectileLot = 6297,
.cannonProjectileLot = 1822,
.cannonSuperChargeSkill = 249,
.cannonSkill = 228,
.cannonTimeout = -1,
.cannonFOV = 58.6,
.useLeaderboards = true,
.streakModifier = 2,
.chargedTime = 10,
.chargedPoints = 25000,
.rewardModelGroup = "QBRewardGroup",
.activityID = 1864,
.scoreRewards = {
{"Score_Reward_1", 50000},
{"Score_Reward_2", 100000},
{"Score_Reward_3", 200000},
{"Score_Reward_4", 400000},
{"Score_Reward_5", 800000},
},
.scoreLootMatrix1 = 157,
.scoreLootMatrix2 = 187,
.scoreLootMatrix3 = 188,
.scoreLootMatrix4 = 189,
.scoreLootMatrix5 = 190,
.firstWaveStartTime = 4.0,
.inBetweenWavePause = 7.0
};
}

View File

@@ -44,15 +44,11 @@ struct SGConstants {
uint32_t chargedPoints;
std::string rewardModelGroup;
uint32_t activityID;
uint32_t scoreReward1;
std::map<std::string, uint32_t> scoreRewards;
uint32_t scoreLootMatrix1;
uint32_t scoreReward2;
uint32_t scoreLootMatrix2;
uint32_t scoreReward3;
uint32_t scoreLootMatrix3;
uint32_t scoreReward4;
uint32_t scoreLootMatrix4;
uint32_t scoreReward5;
uint32_t scoreLootMatrix5;
float_t firstWaveStartTime;
float_t inBetweenWavePause;
@@ -68,6 +64,8 @@ public:
void OnActivityTimerDone(Entity* self, const std::string& name) override;
void OnActivityTimerUpdate(Entity* self, const std::string& name, float_t timeRemaining, float_t elapsedTime) override;
void OnRequestActivityExit(Entity* self, LWOOBJID player, bool canceled) override;
void OnActivityNotify(Entity* self, GameMessages::ActivityNotify& notify) override;
void OnShootingGalleryFire(Entity& self, GameMessages::ShootingGalleryFire& fire) override;
void SuperChargeTimerFunc(Entity* self);
void SpawnWaveTimerFunc(Entity* self);
void EndWaveTimerFunc(Entity* self);
@@ -142,6 +140,7 @@ private:
std::u16string CannonBallSkillIDVariable = u"cbskill";
std::u16string HideSuperChargeVariable = u"HideSuper";
std::u16string AudioFinalWaveDoneVariable = u"Audio_Final_Wave_Done";
std::u16string ModelPercentVariable = u"modelPercent";
std::string SpawnWaveTimer = "SpawnWave";
std::string EndWaveTimer = "EndWave";

View File

@@ -1,4 +1,5 @@
set(DSCRIPTS_SOURCES_AI_RACING)
set(DSCRIPTS_SOURCES_AI_RACING
"RaceImaginationServer.cpp")
add_subdirectory(OBJECTS)
@@ -6,6 +7,24 @@ foreach(file ${DSCRIPTS_SOURCES_AI_RACING_OBJECTS})
set(DSCRIPTS_SOURCES_AI_RACING ${DSCRIPTS_SOURCES_AI_RACING} "OBJECTS/${file}")
endforeach()
add_subdirectory(TRACK_NS)
foreach(file ${DSCRIPTS_SOURCES_AI_RACING_TRACK_NS})
set(DSCRIPTS_SOURCES_AI_RACING ${DSCRIPTS_SOURCES_AI_RACING} "TRACK_NS/${file}")
endforeach()
add_subdirectory(TRACK_GF)
foreach(file ${DSCRIPTS_SOURCES_AI_RACING_TRACK_GF})
set(DSCRIPTS_SOURCES_AI_RACING ${DSCRIPTS_SOURCES_AI_RACING} "TRACK_GF/${file}")
endforeach()
add_subdirectory(TRACK_FV)
foreach(file ${DSCRIPTS_SOURCES_AI_RACING_TRACK_FV})
set(DSCRIPTS_SOURCES_AI_RACING ${DSCRIPTS_SOURCES_AI_RACING} "TRACK_FV/${file}")
endforeach()
add_library(dScriptsAiRacing OBJECT ${DSCRIPTS_SOURCES_AI_RACING})
target_include_directories(dScriptsAiRacing PUBLIC "." "OBJECTS")
target_include_directories(dScriptsAiRacing PUBLIC "." "OBJECTS" "TRACK_NS" "TRACK_GF" "TRACK_FV")
target_precompile_headers(dScriptsAiRacing REUSE_FROM dScriptsBase)

View File

@@ -7,4 +7,5 @@ set(DSCRIPTS_SOURCES_AI_RACING_OBJECTS
"FvRacePillarDServer.cpp"
"FvRaceSmashEggImagineServer.cpp"
"RaceSmashServer.cpp"
"VehicleDeathTriggerWaterServer.cpp"
PARENT_SCOPE)

View File

@@ -0,0 +1,16 @@
#include "VehicleDeathTriggerWaterServer.h"
#include "PossessorComponent.h"
#include "RacingControlComponent.h"
void VehicleDeathTriggerWaterServer::OnCollisionPhantom(Entity* self, Entity* target) {
if (target->IsPlayer() && !target->GetIsDead()) {
const std::vector<Entity*> racingControllers = Game::entityManager->GetEntitiesByComponent(RacingControlComponent::ComponentType);
for (auto* const racingController : racingControllers) {
auto* racingControlComponent = racingController->GetComponent<RacingControlComponent>();
if (racingControlComponent) {
racingControlComponent->OnRequestDie(target, u"death_water");
}
}
}
}

View File

@@ -0,0 +1,11 @@
#ifndef VEHICLEDEATHTRIGGERWATERSERVER_H
#define VEHICLEDEATHTRIGGERWATERSERVER_H
#include "CppScripts.h"
class VehicleDeathTriggerWaterServer : public CppScripts::Script {
public:
void OnCollisionPhantom(Entity* self, Entity* target) override;
};
#endif //!VEHICLEDEATHTRIGGERWATERSERVER_H

View File

@@ -0,0 +1,19 @@
#include "RaceImaginationServer.h"
#include "dZoneManager.h"
void StartSpawner(const std::vector<Spawner*>& spawner) {
for (auto* const entity : spawner) {
entity->Activate();
}
}
void RaceImaginationServer::OnZoneLoadedInfo(Entity* self, const GameMessages::ZoneLoadedInfo& info) {
// Spawn imagination pickups
StartSpawner(Game::zoneManager->GetSpawnersByName("ImaginationSpawn_Min"));
if (info.maxPlayers > 2) {
StartSpawner(Game::zoneManager->GetSpawnersByName("ImaginationSpawn_Med"));
}
if (info.maxPlayers > 4) {
StartSpawner(Game::zoneManager->GetSpawnersByName("ImaginationSpawn_Max"));
}
}

View File

@@ -0,0 +1,11 @@
#ifndef RACEIMAGINATIONSERVER_H
#define RACEIMAGINATIONSERVER_H
#include "CppScripts.h"
class RaceImaginationServer : public virtual CppScripts::Script {
public:
void OnZoneLoadedInfo(Entity* self, const GameMessages::ZoneLoadedInfo& info) override;
};
#endif //!RACEIMAGINATIONSERVER_H

View File

@@ -0,0 +1,3 @@
set(DSCRIPTS_SOURCES_AI_RACING_TRACK_FV
"FvRaceServer.cpp"
PARENT_SCOPE)

View File

@@ -0,0 +1,55 @@
#include "FvRaceServer.h"
#include "RacingControlComponent.h"
#include "Entity.h"
using std::unique_ptr;
using std::make_unique;
void FvRaceServer::OnStartup(Entity* self) {
GameMessages::ConfigureRacingControl config;
auto& raceSet = config.racingSettings;
raceSet.push_back(make_unique<LDFData<std::u16string>>(u"GameType", u"Racing"));
raceSet.push_back(make_unique<LDFData<std::u16string>>(u"GameState", u"Starting"));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Number_Of_PlayersPerTeam", 6));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Minimum_Players_to_Start", 2));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Minimum_Players_for_Group_Achievements", 2));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Car_Object", 7703));
raceSet.push_back(make_unique<LDFData<std::u16string>>(u"Race_PathName", u"MainPath"));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Current_Lap", 1));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Number_of_Laps", 3));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"activityID", 54));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Place_1", 100));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Place_2", 90));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Place_3", 80));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Place_4", 70));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Place_5", 60));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Place_6", 50));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Num_of_Players_1", 15));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Num_of_Players_2", 25));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Num_of_Players_3", 50));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Num_of_Players_4", 85));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Num_of_Players_5", 90));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Num_of_Players_6", 100));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Number_of_Spawn_Groups", 1));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Red_Spawners", 4847));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Blue_Spawners", 4848));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Blue_Flag", 4850));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Red_Flag", 4851));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Red_Point", 4846));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Blue_Point", 4845));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Red_Mark", 4844));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Blue_Mark", 4843));
const std::vector<Entity*> racingControllers = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::RACING_CONTROL);
for (auto* const racingController : racingControllers) {
auto* racingComponent = racingController->GetComponent<RacingControlComponent>();
if (racingComponent) racingComponent->MsgConfigureRacingControl(config);
}
}

View File

@@ -0,0 +1,11 @@
#ifndef FVRACESERVER_H
#define FVRACESERVER_H
#include "RaceImaginationServer.h"
class FvRaceServer : public RaceImaginationServer {
public:
void OnStartup(Entity* self) override;
};
#endif //!FVRACESERVER_H

View File

@@ -0,0 +1,3 @@
set(DSCRIPTS_SOURCES_AI_RACING_TRACK_GF
"GfRaceServer.cpp"
PARENT_SCOPE)

View File

@@ -0,0 +1,55 @@
#include "GfRaceServer.h"
#include "RacingControlComponent.h"
#include "Entity.h"
using std::unique_ptr;
using std::make_unique;
void GfRaceServer::OnStartup(Entity* self) {
GameMessages::ConfigureRacingControl config;
auto& raceSet = config.racingSettings;
raceSet.push_back(make_unique<LDFData<std::u16string>>(u"GameType", u"Racing"));
raceSet.push_back(make_unique<LDFData<std::u16string>>(u"GameState", u"Starting"));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Number_Of_PlayersPerTeam", 6));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Minimum_Players_to_Start", 2));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Minimum_Players_for_Group_Achievements", 2));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Car_Object", 7703));
raceSet.push_back(make_unique<LDFData<std::u16string>>(u"Race_PathName", u"MainPath"));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Current_Lap", 1));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Number_of_Laps", 3));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"activityID", 39));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Place_1", 100));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Place_2", 90));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Place_3", 80));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Place_4", 70));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Place_5", 60));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Place_6", 50));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Num_of_Players_1", 15));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Num_of_Players_2", 25));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Num_of_Players_3", 50));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Num_of_Players_4", 85));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Num_of_Players_5", 90));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Num_of_Players_6", 100));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Number_of_Spawn_Groups", 1));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Red_Spawners", 4847));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Blue_Spawners", 4848));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Blue_Flag", 4850));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Red_Flag", 4851));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Red_Point", 4846));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Blue_Point", 4845));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Red_Mark", 4844));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Blue_Mark", 4843));
const std::vector<Entity*> racingControllers = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::RACING_CONTROL);
for (auto* const racingController : racingControllers) {
auto* racingComponent = racingController->GetComponent<RacingControlComponent>();
if (racingComponent) racingComponent->MsgConfigureRacingControl(config);
}
}

View File

@@ -0,0 +1,11 @@
#ifndef GFRACESERVER_H
#define GFRACESERVER_H
#include "RaceImaginationServer.h"
class GfRaceServer : public RaceImaginationServer {
public:
void OnStartup(Entity* self) override;
};
#endif //!GFRACESERVER_H

View File

@@ -0,0 +1,3 @@
set(DSCRIPTS_SOURCES_AI_RACING_TRACK_NS
"NsRaceServer.cpp"
PARENT_SCOPE)

View File

@@ -0,0 +1,54 @@
#include "NsRaceServer.h"
#include "RacingControlComponent.h"
#include "Entity.h"
using std::unique_ptr;
using std::make_unique;
void NsRaceServer::OnStartup(Entity* self) {
GameMessages::ConfigureRacingControl config;
auto& raceSet = config.racingSettings;
raceSet.push_back(make_unique<LDFData<std::u16string>>(u"GameType", u"Racing"));
raceSet.push_back(make_unique<LDFData<std::u16string>>(u"GameState", u"Starting"));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Number_Of_PlayersPerTeam", 6));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Minimum_Players_to_Start", 2));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Minimum_Players_for_Group_Achievements", 2));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Car_Object", 7703));
raceSet.push_back(make_unique<LDFData<std::u16string>>(u"Race_PathName", u"MainPath"));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Current_Lap", 1));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Number_of_Laps", 3));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"activityID", 42));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Place_1", 100));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Place_2", 90));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Place_3", 80));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Place_4", 70));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Place_5", 60));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Place_6", 50));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Num_of_Players_1", 15));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Num_of_Players_2", 25));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Num_of_Players_3", 50));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Num_of_Players_4", 85));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Num_of_Players_5", 90));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Num_of_Players_6", 100));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Number_of_Spawn_Groups", 1));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Red_Spawners", 4847));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Blue_Spawners", 4848));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Blue_Flag", 4850));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Red_Flag", 4851));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Red_Point", 4846));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Blue_Point", 4845));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Red_Mark", 4844));
raceSet.push_back(make_unique<LDFData<int32_t>>(u"Blue_Mark", 4843));
std::vector<Entity*> racingControllers = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::RACING_CONTROL);
for (auto* const racingController : racingControllers) {
auto* racingComponent = racingController->GetComponent<RacingControlComponent>();
if (racingComponent) racingComponent->MsgConfigureRacingControl(config);
}
}

View File

@@ -0,0 +1,12 @@
#ifndef NSRACESERVER_H
#define NSRACESERVER_H
#include "CppScripts.h"
#include "RaceImaginationServer.h"
class NsRaceServer : public RaceImaginationServer {
public:
void OnStartup(Entity* self) override;
};
#endif //!NSRACESERVER_H

View File

@@ -13,7 +13,7 @@ void Server::SetupLogger(const std::string_view serviceName) {
const auto logsDir = BinaryPathFinder::GetBinaryDir() / "logs";
if (!std::filesystem::exists(logsDir)) std::filesystem::create_directory(logsDir);
if (!std::filesystem::exists(logsDir)) std::filesystem::create_directories(logsDir);
std::string logPath = (logsDir / serviceName).string() + "_" + std::to_string(time(nullptr)) + ".log";
bool logToConsole = false;

View File

@@ -267,41 +267,31 @@ int main(int argc, char** argv) {
// pre calculate the FDB checksum
if (Game::config->GetValue("check_fdb") == "1") {
std::ifstream fileStream;
auto cdclient = Game::assetManager->GetFile("cdclient.fdb");
if (cdclient) {
static const std::vector<std::string> aliases = {
"CDServers.fdb",
"cdserver.fdb",
"CDClient.fdb",
"cdclient.fdb",
};
const int32_t bufferSize = 1024;
MD5 md5;
for (const auto& file : aliases) {
fileStream.open(Game::assetManager->GetResPath() / file, std::ios::binary | std::ios::in);
if (fileStream.is_open()) {
break;
char fileStreamBuffer[bufferSize] = {};
while (!cdclient.eof()) {
memset(fileStreamBuffer, 0, bufferSize);
cdclient.read(fileStreamBuffer, bufferSize);
md5.update(fileStreamBuffer, cdclient.gcount());
}
const char* nullTerminateBuffer = "\0";
md5.update(nullTerminateBuffer, 1); // null terminate the data
md5.finalize();
databaseChecksum = md5.hexdigest();
LOG("FDB Checksum calculated as: %s", databaseChecksum.c_str());
}
const int32_t bufferSize = 1024;
MD5 md5;
char fileStreamBuffer[1024] = {};
while (!fileStream.eof()) {
memset(fileStreamBuffer, 0, bufferSize);
fileStream.read(fileStreamBuffer, bufferSize);
md5.update(fileStreamBuffer, fileStream.gcount());
if (databaseChecksum.empty()) {
LOG("check_fdb is on but no fdb file found.");
return EXIT_FAILURE;
}
fileStream.close();
const char* nullTerminateBuffer = "\0";
md5.update(nullTerminateBuffer, 1); // null terminate the data
md5.finalize();
databaseChecksum = md5.hexdigest();
LOG("FDB Checksum calculated as: %s", databaseChecksum.c_str());
}
uint32_t currentFrameDelta = highFrameDelta;
@@ -548,115 +538,115 @@ void HandlePacketChat(Packet* packet) {
if (packet->data[0] == ID_USER_PACKET_ENUM && packet->length >= 4) {
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::CHAT) {
switch (static_cast<MessageType::Chat>(packet->data[3])) {
case MessageType::Chat::WORLD_ROUTE_PACKET: {
CINSTREAM_SKIP_HEADER;
LWOOBJID playerID;
inStream.Read(playerID);
case MessageType::Chat::WORLD_ROUTE_PACKET: {
CINSTREAM_SKIP_HEADER;
LWOOBJID playerID;
inStream.Read(playerID);
auto player = Game::entityManager->GetEntity(playerID);
if (!player) return;
auto player = Game::entityManager->GetEntity(playerID);
if (!player) return;
auto sysAddr = player->GetSystemAddress();
auto sysAddr = player->GetSystemAddress();
//Write our stream outwards:
CBITSTREAM;
unsigned char data;
while (inStream.Read(data)) {
bitStream.Write(data);
}
SEND_PACKET; //send routed packet to player
break;
//Write our stream outwards:
CBITSTREAM;
unsigned char data;
while (inStream.Read(data)) {
bitStream.Write(data);
}
case MessageType::Chat::GM_ANNOUNCE: {
CINSTREAM_SKIP_HEADER;
SEND_PACKET; //send routed packet to player
break;
}
std::string title;
std::string msg;
case MessageType::Chat::GM_ANNOUNCE: {
CINSTREAM_SKIP_HEADER;
uint32_t len;
inStream.Read<uint32_t>(len);
for (uint32_t i = 0; len > i; i++) {
char character;
inStream.Read<char>(character);
title += character;
}
std::string title;
std::string msg;
len = 0;
inStream.Read<uint32_t>(len);
for (uint32_t i = 0; len > i; i++) {
char character;
inStream.Read<char>(character);
msg += character;
}
uint32_t len;
inStream.Read<uint32_t>(len);
for (uint32_t i = 0; len > i; i++) {
char character;
inStream.Read<char>(character);
title += character;
}
//Send to our clients:
AMFArrayValue args;
len = 0;
inStream.Read<uint32_t>(len);
for (uint32_t i = 0; len > i; i++) {
char character;
inStream.Read<char>(character);
msg += character;
}
args.Insert("title", title);
args.Insert("message", msg);
//Send to our clients:
AMFArrayValue args;
GameMessages::SendUIMessageServerToAllClients("ToggleAnnounce", args);
args.Insert("title", title);
args.Insert("message", msg);
GameMessages::SendUIMessageServerToAllClients("ToggleAnnounce", args);
break;
}
case MessageType::Chat::GM_MUTE: {
CINSTREAM_SKIP_HEADER;
LWOOBJID playerId;
time_t expire = 0;
inStream.Read(playerId);
inStream.Read(expire);
auto* entity = Game::entityManager->GetEntity(playerId);
auto* character = entity != nullptr ? entity->GetCharacter() : nullptr;
auto* user = character != nullptr ? character->GetParentUser() : nullptr;
if (user) {
user->SetMuteExpire(expire);
entity->GetCharacter()->SendMuteNotice();
}
break;
}
case MessageType::Chat::TEAM_GET_STATUS: {
CINSTREAM_SKIP_HEADER;
LWOOBJID teamID = 0;
char lootOption = 0;
char memberCount = 0;
std::vector<LWOOBJID> members;
inStream.Read(teamID);
bool deleteTeam = inStream.ReadBit();
if (deleteTeam) {
TeamManager::Instance()->DeleteTeam(teamID);
LOG("Deleting team (%llu)", teamID);
break;
}
case MessageType::Chat::GM_MUTE: {
CINSTREAM_SKIP_HEADER;
LWOOBJID playerId;
time_t expire = 0;
inStream.Read(playerId);
inStream.Read(expire);
inStream.Read(lootOption);
inStream.Read(memberCount);
LOG("Updating team (%llu), (%i), (%i)", teamID, lootOption, memberCount);
for (char i = 0; i < memberCount; i++) {
LWOOBJID member = LWOOBJID_EMPTY;
inStream.Read(member);
members.push_back(member);
auto* entity = Game::entityManager->GetEntity(playerId);
auto* character = entity != nullptr ? entity->GetCharacter() : nullptr;
auto* user = character != nullptr ? character->GetParentUser() : nullptr;
if (user) {
user->SetMuteExpire(expire);
entity->GetCharacter()->SendMuteNotice();
}
break;
LOG("Updating team member (%llu)", member);
}
case MessageType::Chat::TEAM_GET_STATUS: {
CINSTREAM_SKIP_HEADER;
TeamManager::Instance()->UpdateTeam(teamID, lootOption, members);
LWOOBJID teamID = 0;
char lootOption = 0;
char memberCount = 0;
std::vector<LWOOBJID> members;
inStream.Read(teamID);
bool deleteTeam = inStream.ReadBit();
if (deleteTeam) {
TeamManager::Instance()->DeleteTeam(teamID);
LOG("Deleting team (%llu)", teamID);
break;
}
inStream.Read(lootOption);
inStream.Read(memberCount);
LOG("Updating team (%llu), (%i), (%i)", teamID, lootOption, memberCount);
for (char i = 0; i < memberCount; i++) {
LWOOBJID member = LWOOBJID_EMPTY;
inStream.Read(member);
members.push_back(member);
LOG("Updating team member (%llu)", member);
}
TeamManager::Instance()->UpdateTeam(teamID, lootOption, members);
break;
}
default:
LOG("Received an unknown chat: %i", int(packet->data[3]));
break;
}
default:
LOG("Received an unknown chat: %i", int(packet->data[3]));
}
}
}

View File

@@ -7,15 +7,5 @@ master_server_port=2000
# The port number to start world servers on. Will be incremented for each world
world_port_start=3000
# Use sudo when launching the auth server.
# Required by default if on Linux as auth runs on port 1001
use_sudo_auth=1
# Use sudo when launching the chat server
use_sudo_chat=0
# Use sudo when launching world servers
use_sudo_world=0
# 0 or 1, should autostart auth, chat, and char servers
prestart_servers=1

View File

@@ -28,7 +28,8 @@ client_location=
# The maximum outgoing bandwidth in bits. If your clients are having
# issues with enemies taking a while to catch up to them, increse this value.
maximum_outgoing_bandwidth=80000
# Empty or 0 means no limit
maximum_outgoing_bandwidth=0
# The Maximum Translation Unit (MTU) size for packets. If players are
# getting stuck at 55% on the loading screen, lower this number to