commit
a8c9943ac9
|
@ -7,6 +7,9 @@
|
|||
[submodule "Phoenix/ThirdParty/entt"]
|
||||
path = Phoenix/ThirdParty/entt
|
||||
url = https://github.com/skypjack/entt.git
|
||||
[submodule "Phoenix/ThirdParty/enet"]
|
||||
path = Phoenix/ThirdParty/enet
|
||||
url = https://github.com/lsalzman/enet.git
|
||||
[submodule "Phoenix/ThirdParty/json"]
|
||||
path = Phoenix/ThirdParty/json
|
||||
url = https://github.com/nlohmann/json.git
|
||||
|
|
|
@ -73,4 +73,6 @@ available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.ht
|
|||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
||||
https://www.contributor-covenant.org/faq
|
||||
|
||||
#### </b> {#codeofconduct}
|
||||
|
|
|
@ -131,7 +131,7 @@ Multiline comments should use the /* */ method of commenting, where the /* and *
|
|||
When documenting classes, methods and similar we follow the Doxygen Javadoc syntax. For example, this convention should be followed:
|
||||
```cpp
|
||||
/**
|
||||
* @brief This class is for documentation reasons.
|
||||
* @brief This class is for documentation reasons.
|
||||
*/
|
||||
class FooBar {
|
||||
public:
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
# Phoenix Architecture
|
||||
The goal of these documents is to provide an understanding of the architecture of Phoenix. If you're just starting to
|
||||
contribute to this project, this is a good place to start to figure out how things work. If you have questions that are
|
||||
not covered by these documents, reach out to [\#help](https://discord.gg/bPHVcxv) in our discord.
|
||||
* [Networking][networking]
|
||||
|
||||
|
||||
[networking]: Networking.md
|
||||
[networking]: @ref networking
|
||||
|
||||
#### </b> {#architecture}
|
|
@ -829,7 +829,7 @@ WARN_LOGFILE =
|
|||
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
|
||||
# Note: If this tag is empty the current directory is searched.
|
||||
|
||||
INPUT = ../Phoenix
|
||||
INPUT = ../
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
||||
|
@ -871,7 +871,7 @@ FILE_PATTERNS = *.c \
|
|||
*.md \
|
||||
*.mm \
|
||||
*.dox \
|
||||
*.doc
|
||||
*.doc
|
||||
|
||||
# The RECURSIVE tag can be used to specify whether or not subdirectories should
|
||||
# be searched for input files as well.
|
||||
|
@ -995,7 +995,7 @@ FILTER_SOURCE_PATTERNS =
|
|||
# (index.html). This can be useful if you have a project on for instance GitHub
|
||||
# and want to reuse the introduction page also for the doxygen output.
|
||||
|
||||
USE_MDFILE_AS_MAINPAGE = README.md
|
||||
USE_MDFILE_AS_MAINPAGE = ../README.md
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to source browsing
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
# Networking Architecure
|
||||
This is a generic overview of how the networking system works. If you have additional questions beyond what is
|
||||
described here, reach out in [#networking](https://discord.gg/6vwjZhC) on Discord. If you are interested in contributing
|
||||
to the networking system, this is also the place to reach out to coordinate any work you want to do.
|
||||
## Flow
|
||||
The data in the networking system is managed in a deterministic lockstep method. This means the server is 100%
|
||||
authoritative as to what happens. For some background information on deterministic lockstep check out
|
||||
[this article by GlenFelder (Gaffer On Games) is a good read](https://gafferongames.com/post/deterministic_lockstep/).
|
||||
We process three types of information during runtime. States are non-critical but time-sensitive pieces of information
|
||||
that get sent unreliably and are discarded when not received (such as positioning). Events are critical pieces of
|
||||
information that are sent via Enet's reliable packet system (Such as when a block is broken). Messages are essentially
|
||||
events but we use a third channel on Enet since they are processed by an entirely separate system.
|
||||
NOTE: This system's V1 version is a WIP, some information here may describe a future state of the networking system not
|
||||
yet implemented.
|
||||
### States
|
||||
[InputState]s are created by the client every ∆T / game tick (1/20 of a second by default) and queued for transmission.
|
||||
This happens in a thread so we never run out of time before the next ∆T.
|
||||
The networking system is then running in another thread `m_iris` watching that queue. It packs states into redundant
|
||||
packages (of 5 by default) so each packet will include the X most recent states. This helps prevent packet loss from
|
||||
becoming an issue.
|
||||
When the server gets packets, the networking thread (`m_iris` again) unpacks the data and fills a new queue system with
|
||||
any packets it doesn't already have discarding any data it does have or arrived too late. This queue system contains
|
||||
StateBundles which are a bundle of InputStates, one from each player for that tick (sequence). When either we have an
|
||||
InputState from each connected player, or we have waited too long (.5 seconds max by default) we mark that bundle ready
|
||||
for consumption.
|
||||
|
||||
A separate game thread on the server then watches that queue for when the networking system has marked that the oldest
|
||||
stateBundle in the queue is ready. When it is the game thread takes that bundle, as well as any queued events or
|
||||
messages, and processes them moving the server forward a tick. Once a tick is done processing, the server blasts any
|
||||
relevant information to clients to clients updating them on the final official state for that tick.
|
||||
|
||||
When a client gets information back its usually significantly (up to 2.5 seconds at the extreme by default) later than
|
||||
it sent that packet. While the client is waiting for that state from the server, it is predicting what is happening
|
||||
based on velocities, AI logic, or the known player input state. When the client does get the packet for that state, it
|
||||
checks to see if its predictions up until that point were correct, if they aren't (within a margin of error) then it
|
||||
re-processes every state from that confirmation re-predicting all future states up to the one the client is currently
|
||||
on.
|
||||
|
||||
### Events
|
||||
Events are fortunately a lot simpler than States. Events are things such as an inventory movement (client to server) or
|
||||
a block breaking (server to client). We use events when the player loads a new chunk to avoid sending all loaded
|
||||
chunks every game-tick (we only update data if it changes).
|
||||
|
||||
Events aren't always a circular path, but can be. When an event occurs on the client, lets take inventory movement for
|
||||
example, we send a message to the server letting the server know it happened. The server then processes that event
|
||||
determining if that was a valid event, and sends an update event back to the client to confirm that it happened, or a
|
||||
rejection event back to the client saying it didn't happen. If it happened then the client goes along with its day, if
|
||||
it didn't actually happen, we roll back that action.
|
||||
|
||||
When the client sends the server information, it just sends a packet and the server processes that event on the next
|
||||
game tick. When the server sends the client an event, it includes the tick number in case the client needs to do any
|
||||
rollback. This means, the event may happen at a slightly different time on the client as the server (which is okay as
|
||||
long as the event did actually happen). Not binding the events to ticks on the client side, helps allow the game ticks
|
||||
to run freely and unbound if the client has the power to do so (of if the client does not have enough power, its okay
|
||||
for it to lag without doing much thinking).
|
||||
|
||||
### Messages
|
||||
Messages are just like events with a few key differences.
|
||||
* When processed on the server, they are always sent to the Commander so they don't have header information determining
|
||||
what system they go to.
|
||||
* Messages dont have to have a return to the client to ack being received. We trust Enet's reliable packet system to
|
||||
ensure they are delivered. Its up to the Commander/ the specific command to send the error message back when something
|
||||
goes wrong.
|
||||
* Messages from the Server to the Client are purely information and printed to the terminal with little to no client
|
||||
side processing. If a Message from the client results in an action (EX: /tp) then there is no client side prediction and
|
||||
the Event system is instead used to relay that the action happened.
|
||||
|
||||
|
||||
[InputState]: @ref phx::InputState
|
||||
|
||||
#### </b> {#networking}
|
|
@ -4,21 +4,23 @@ add_subdirectory(Audio)
|
|||
|
||||
set(currentDir ${CMAKE_CURRENT_LIST_DIR})
|
||||
set(Headers
|
||||
${eventHeaders}
|
||||
${graphicsHeaders}
|
||||
${audioHeaders}
|
||||
${eventHeaders}
|
||||
${graphicsHeaders}
|
||||
${audioHeaders}
|
||||
|
||||
${currentDir}/Player.hpp
|
||||
${currentDir}/Player.hpp
|
||||
${currentDir}/InputQueue.hpp
|
||||
|
||||
${currentDir}/Client.hpp
|
||||
${currentDir}/SplashScreen.hpp
|
||||
${currentDir}/Crosshair.hpp
|
||||
${currentDir}/Game.hpp
|
||||
${currentDir}/EscapeMenu.hpp
|
||||
${currentDir}/InputMap.hpp
|
||||
${currentDir}/Client.hpp
|
||||
${currentDir}/SplashScreen.hpp
|
||||
${currentDir}/Crosshair.hpp
|
||||
${currentDir}/Game.hpp
|
||||
${currentDir}/EscapeMenu.hpp
|
||||
${currentDir}/InputMap.hpp
|
||||
${currentDir}/Network.hpp
|
||||
|
||||
${currentDir}/DebugOverlay.hpp
|
||||
${currentDir}/GameTools.hpp
|
||||
${currentDir}/DebugOverlay.hpp
|
||||
${currentDir}/GameTools.hpp
|
||||
|
||||
PARENT_SCOPE
|
||||
)
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
|
|
@ -28,18 +28,20 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <Client/Crosshair.hpp>
|
||||
#include <Client/EscapeMenu.hpp>
|
||||
#include <Client/GameTools.hpp>
|
||||
#include <Client/Graphics/Camera.hpp>
|
||||
#include <Client/Graphics/Layer.hpp>
|
||||
#include <Client/Graphics/ShaderPipeline.hpp>
|
||||
#include <Client/Graphics/UI.hpp>
|
||||
#include <Client/Graphics/Window.hpp>
|
||||
#include <Client/Player.hpp>
|
||||
#include <Client/EscapeMenu.hpp>
|
||||
#include <Client/Crosshair.hpp>
|
||||
#include <Client/InputQueue.hpp>
|
||||
|
||||
#include <Common/CMS/ModManager.hpp>
|
||||
|
||||
#include <deque>
|
||||
|
||||
namespace phx::client
|
||||
{
|
||||
/**
|
||||
|
@ -69,11 +71,20 @@ namespace phx::client
|
|||
void onEvent(events::Event& e) override;
|
||||
void tick(float dt) override;
|
||||
|
||||
/** @brief Sends a message packet to the server for the commander to
|
||||
* interpret.
|
||||
*
|
||||
* @param input The message sent to the server
|
||||
* @param cout Needs to be depreciated, unused (but required by
|
||||
* terminal)
|
||||
*/
|
||||
void sendMessage(const std::string& input, std::ostringstream& cout);
|
||||
|
||||
private:
|
||||
gfx::Window* m_window;
|
||||
gfx::FPSCamera* m_camera = nullptr;
|
||||
entt::registry* m_registry;
|
||||
Player* m_player;
|
||||
entt::registry* m_registry;
|
||||
Player* m_player;
|
||||
voxels::ChunkView* m_world = nullptr;
|
||||
|
||||
gfx::ShaderPipeline m_renderPipeline;
|
||||
|
@ -81,17 +92,20 @@ namespace phx::client
|
|||
ui::ChatWindow* m_chat = nullptr;
|
||||
|
||||
cms::ModManager* m_modManager;
|
||||
|
||||
Crosshair* m_crosshair = nullptr;
|
||||
|
||||
Crosshair* m_crosshair = nullptr;
|
||||
EscapeMenu* m_escapeMenu = nullptr;
|
||||
GameTools* m_gameDebug = nullptr;
|
||||
bool m_followCam = true;
|
||||
math::vec3 m_prevPos;
|
||||
int m_playerHand = 0;
|
||||
GameTools* m_gameDebug = nullptr;
|
||||
bool m_followCam = true;
|
||||
math::vec3 m_prevPos;
|
||||
int m_playerHand = 0;
|
||||
|
||||
client::Network* m_network;
|
||||
client::InputQueue* m_inputQueue;
|
||||
|
||||
// intermediary variables to prevent getting the pointer from the client
|
||||
// singleton every tick.
|
||||
audio::Audio* m_audio;
|
||||
audio::Audio* m_audio;
|
||||
audio::Listener* m_listener;
|
||||
};
|
||||
} // namespace phx::client
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
// Copyright 2019-20 Genten Studios
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Client/InputMap.hpp>
|
||||
#include <Client/Network.hpp>
|
||||
#include <Client/Player.hpp>
|
||||
|
||||
#include <Common/Util/BlockingQueue.hpp>
|
||||
|
||||
namespace phx::client
|
||||
{
|
||||
class InputQueue
|
||||
{
|
||||
public:
|
||||
InputQueue(entt::registry* registry, Player* player);
|
||||
~InputQueue();
|
||||
|
||||
/**
|
||||
* @brief Thread to capture and queue input states
|
||||
* @param dt How frequently in seconds a state should be captured in
|
||||
* milliseconds
|
||||
* @param network Pointer to network to send states to
|
||||
*/
|
||||
private:
|
||||
void run(std::chrono::milliseconds dt, client::Network* network);
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Starts a new thread to capture and queue input states
|
||||
* @param dt How frequently in seconds a state should be captured in
|
||||
* milliseconds
|
||||
* @param network Pointer to network to send states to
|
||||
*/
|
||||
void start(std::chrono::milliseconds dt, client::Network* network);
|
||||
|
||||
/**
|
||||
* @brief Stops the current thread
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/**
|
||||
* @brief Gets the current state of the input
|
||||
* @return The input state
|
||||
*/
|
||||
InputState getCurrentState();
|
||||
|
||||
BlockingQueue<InputState> m_queue;
|
||||
|
||||
private:
|
||||
bool m_running;
|
||||
std::thread m_thread;
|
||||
|
||||
Player* m_player;
|
||||
entt::registry* m_registry;
|
||||
|
||||
std::size_t m_sequence;
|
||||
|
||||
client::Input* m_forward;
|
||||
client::Input* m_backward;
|
||||
client::Input* m_left;
|
||||
client::Input* m_right;
|
||||
client::Input* m_up;
|
||||
client::Input* m_down;
|
||||
};
|
||||
} // namespace phx::client
|
|
@ -0,0 +1,99 @@
|
|||
// Copyright 2019-20 Genten Studios
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Common/Input.hpp>
|
||||
#include <Common/Network/Host.hpp>
|
||||
#include <Common/Util/BlockingQueue.hpp>
|
||||
#include <thread>
|
||||
|
||||
namespace phx::client
|
||||
{
|
||||
class Network
|
||||
{
|
||||
public:
|
||||
Network(std::ostringstream& chat);
|
||||
~Network();
|
||||
|
||||
private:
|
||||
void run();
|
||||
|
||||
public:
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Actions taken when a state is received
|
||||
*
|
||||
* @param data The data in the state packet
|
||||
* @param dataLength The length of the data in the state packet
|
||||
*/
|
||||
void parseEvent(net::Packet& packet);
|
||||
|
||||
/**
|
||||
* @brief Actions taken when a state is received
|
||||
*
|
||||
* @param data The data in the state packet
|
||||
* @param dataLength The length of the data in the state packet
|
||||
*/
|
||||
void parseState(net::Packet& packet);
|
||||
|
||||
/**
|
||||
* @brief Actions taken when a message is received
|
||||
*
|
||||
* @param data The data in the message packet
|
||||
* @param dataLength The length of the data in the message packet
|
||||
*/
|
||||
void parseMessage(phx::net::Packet& packet);
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Sends a state packet to a client
|
||||
*
|
||||
* @param userRef The user to sent the state to
|
||||
* @param data The state packet data
|
||||
*/
|
||||
void sendState(InputState inputState);
|
||||
|
||||
/**
|
||||
* @brief Sends a message packet to a client
|
||||
*
|
||||
* @param userRef The user to sent the message to
|
||||
* @param data The message packet data
|
||||
*/
|
||||
void sendMessage(std::string message);
|
||||
|
||||
private:
|
||||
bool m_running;
|
||||
phx::net::Host* m_client;
|
||||
std::ostringstream& m_chat;
|
||||
std::thread m_thread;
|
||||
};
|
||||
} // namespace phx::client::net
|
|
@ -3,22 +3,24 @@ add_subdirectory(Audio)
|
|||
|
||||
set(currentDir ${CMAKE_CURRENT_LIST_DIR})
|
||||
set(Sources
|
||||
${graphicsSources}
|
||||
${audioSources}
|
||||
${graphicsSources}
|
||||
${audioSources}
|
||||
|
||||
${currentDir}/Player.cpp
|
||||
${currentDir}/Player.cpp
|
||||
${currentDir}/InputQueue.cpp
|
||||
|
||||
${currentDir}/Client.cpp
|
||||
${currentDir}/SplashScreen.cpp
|
||||
${currentDir}/Crosshair.cpp
|
||||
${currentDir}/Game.cpp
|
||||
${currentDir}/EscapeMenu.cpp
|
||||
${currentDir}/InputMap.cpp
|
||||
${currentDir}/Client.cpp
|
||||
${currentDir}/SplashScreen.cpp
|
||||
${currentDir}/Crosshair.cpp
|
||||
${currentDir}/Game.cpp
|
||||
${currentDir}/EscapeMenu.cpp
|
||||
${currentDir}/InputMap.cpp
|
||||
${currentDir}/Network.cpp
|
||||
|
||||
${currentDir}/DebugOverlay.cpp
|
||||
${currentDir}/GameTools.cpp
|
||||
${currentDir}/DebugOverlay.cpp
|
||||
${currentDir}/GameTools.cpp
|
||||
|
||||
${currentDir}/Main.cpp
|
||||
${currentDir}/Main.cpp
|
||||
|
||||
PARENT_SCOPE
|
||||
)
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
|
|
@ -119,11 +119,13 @@ void Client::onEvent(events::Event e)
|
|||
void Client::run()
|
||||
{
|
||||
Settings::get()->load("settings.txt");
|
||||
Logger::initialize({});
|
||||
LoggerConfig config;
|
||||
config.verbosity = LogVerbosity::DEBUG;
|
||||
Logger::initialize(config);
|
||||
|
||||
audio::Audio::initialize();
|
||||
m_audio = new audio::Audio();
|
||||
|
||||
|
||||
SplashScreen* splashScreen = new SplashScreen();
|
||||
m_layerStack.pushLayer(splashScreen);
|
||||
|
||||
|
|
|
@ -29,11 +29,14 @@
|
|||
#include <Client/Client.hpp>
|
||||
#include <Client/Game.hpp>
|
||||
|
||||
#include <Common/Actor.hpp>
|
||||
#include <Common/CMS/ModManager.hpp>
|
||||
#include <Common/Serialization/Serializer.hpp>
|
||||
#include <Common/Voxels/BlockRegistry.hpp>
|
||||
|
||||
#include <Common/Actor.hpp>
|
||||
#include <Common/Commander.hpp>
|
||||
#include <Common/Position.hpp>
|
||||
#include <Common/Voxels/BlockRegistry.hpp>
|
||||
#include <Common/Logger.hpp>
|
||||
|
||||
#include <cmath>
|
||||
#include <tuple>
|
||||
|
@ -41,11 +44,18 @@
|
|||
using namespace phx::client;
|
||||
using namespace phx;
|
||||
|
||||
static Commander kirk;
|
||||
///@todo This needs refactored to play nicely
|
||||
/**
|
||||
* This exists so we can call the message function from the chat client,
|
||||
* we definitely need to just clean that up so it all plays nicely. If
|
||||
* a second instance of the game is created, this entire system will break
|
||||
* (but so will a few others . . . )
|
||||
*/
|
||||
static Game* myGame = nullptr;
|
||||
|
||||
static void rawEcho(const std::string& input, std::ostringstream& cout)
|
||||
{
|
||||
kirk.callback(input, cout);
|
||||
myGame->sendMessage(input, cout);
|
||||
}
|
||||
|
||||
Game::Game(gfx::Window* window, entt::registry* registry)
|
||||
|
@ -59,7 +69,7 @@ Game::Game(gfx::Window* window, entt::registry* registry)
|
|||
fileStream.open("Saves/" + save + "/Mods.txt");
|
||||
if (!fileStream.is_open())
|
||||
{
|
||||
std::cout << "Error opening save file";
|
||||
LOG_FATAL("CMS") << "Error opening save file";
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
@ -73,6 +83,10 @@ Game::Game(gfx::Window* window, entt::registry* registry)
|
|||
|
||||
voxels::BlockRegistry::get()->registerAPI(m_modManager);
|
||||
|
||||
m_modManager->registerFunction(
|
||||
"core.command.register",
|
||||
[](std::string command, std::string help, sol::function f) {});
|
||||
|
||||
m_modManager->registerFunction("core.print", [=](const std::string& text) {
|
||||
m_chat->cout << text << "\n";
|
||||
});
|
||||
|
@ -156,7 +170,7 @@ Game::Game(gfx::Window* window, entt::registry* registry)
|
|||
float x = source["position"]["x"];
|
||||
float y = source["position"]["y"];
|
||||
float z = source["position"]["z"];
|
||||
|
||||
|
||||
audioSource.setPos({x, y, z});
|
||||
}
|
||||
|
||||
|
@ -230,19 +244,23 @@ Game::Game(gfx::Window* window, entt::registry* registry)
|
|||
|
||||
Client::get()->getAudioPool()->queue(audioSource);
|
||||
});
|
||||
|
||||
myGame = this;
|
||||
}
|
||||
|
||||
Game::~Game() { delete m_chat; }
|
||||
|
||||
void Game::onAttach()
|
||||
{
|
||||
/// @todo Replace this with logger
|
||||
printf("%s", "Attaching game layer\n");
|
||||
m_chat = new ui::ChatWindow("Chat Window", 5,
|
||||
"Type /help for a command list and help.");
|
||||
|
||||
/// @TODO replace with network callback
|
||||
m_chat->registerCallback(rawEcho);
|
||||
|
||||
m_network = new client::Network(m_chat->cout);
|
||||
m_network->start();
|
||||
|
||||
m_player = new Player(m_registry);
|
||||
m_player->registerAPI(m_modManager);
|
||||
|
||||
|
@ -251,11 +269,11 @@ void Game::onAttach()
|
|||
|
||||
if (!result.ok)
|
||||
{
|
||||
LOG_FATAL("MODDING") << "An error has occured.";
|
||||
LOG_FATAL("MODDING") << "An error has occurred.";
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
printf("%s", "Registering world\n");
|
||||
LOG_INFO("MAIN") << "Registering world";
|
||||
const std::string save = "save1";
|
||||
m_world = new voxels::ChunkView(3, voxels::Map(save, "map1"));
|
||||
m_player->setWorld(m_world);
|
||||
|
@ -266,7 +284,7 @@ void Game::onAttach()
|
|||
m_player->getEntity(),
|
||||
voxels::BlockRegistry::get()->getFromRegistryID(0));
|
||||
|
||||
printf("%s", "Prepare rendering\n");
|
||||
LOG_INFO("MAIN") << "Prepare rendering";
|
||||
m_renderPipeline.prepare("Assets/SimpleWorld.vert",
|
||||
"Assets/SimpleWorld.frag",
|
||||
gfx::ChunkRenderer::getRequiredShaderLayout());
|
||||
|
@ -276,7 +294,7 @@ void Game::onAttach()
|
|||
const math::mat4 model;
|
||||
m_renderPipeline.setMatrix("u_model", model);
|
||||
|
||||
printf("%s", "Register GUI\n");
|
||||
LOG_INFO("MAIN") << "Register GUI";
|
||||
m_crosshair = new Crosshair(m_window);
|
||||
m_escapeMenu = new EscapeMenu(m_window);
|
||||
Client::get()->pushLayer(m_crosshair);
|
||||
|
@ -287,14 +305,20 @@ void Game::onAttach()
|
|||
new GameTools(&m_followCam, &m_playerHand, m_player, m_registry);
|
||||
Client::get()->pushLayer(m_gameDebug);
|
||||
}
|
||||
printf("%s", "Game layer attached");
|
||||
|
||||
m_inputQueue = new InputQueue(m_registry, m_player);
|
||||
m_inputQueue->start(std::chrono::milliseconds(50), m_network);
|
||||
|
||||
LOG_INFO("MAIN") << "Game layer attached";
|
||||
}
|
||||
|
||||
void Game::onDetach()
|
||||
{
|
||||
m_network->stop();
|
||||
delete m_world;
|
||||
delete m_player;
|
||||
delete m_camera;
|
||||
delete m_network;
|
||||
}
|
||||
|
||||
void Game::onEvent(events::Event& e)
|
||||
|
@ -402,7 +426,7 @@ void Game::tick(float dt)
|
|||
m_camera->tick(dt);
|
||||
|
||||
const Position& position = m_registry->get<Position>(m_player->getEntity());
|
||||
|
||||
|
||||
if (m_followCam)
|
||||
{
|
||||
m_prevPos = position.position;
|
||||
|
@ -425,4 +449,9 @@ void Game::tick(float dt)
|
|||
m_world->render();
|
||||
m_player->renderSelectionBox(m_camera->calculateViewMatrix(),
|
||||
m_camera->getProjection());
|
||||
}
|
||||
}
|
||||
|
||||
void Game::sendMessage(const std::string& input, std::ostringstream& cout)
|
||||
{
|
||||
m_network->sendMessage(input);
|
||||
}
|
||||
|
|
|
@ -188,4 +188,3 @@ void FPSCamera::onWindowResize(events::Event e)
|
|||
}
|
||||
|
||||
void FPSCamera::setActor(entt::entity actor) { m_actor = actor;}
|
||||
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
// Copyright 2019-20 Genten Studios
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <Client/InputQueue.hpp>
|
||||
|
||||
#include <Common/Position.hpp>
|
||||
|
||||
using namespace phx::client;
|
||||
using namespace phx;
|
||||
|
||||
InputQueue::InputQueue(entt::registry* registry, Player* player)
|
||||
: m_player(player), m_registry(registry),
|
||||
|
||||
m_forward(InputMap::get()->getInput("core.move.forward")),
|
||||
m_backward(InputMap::get()->getInput("core.move.backward")),
|
||||
m_left(InputMap::get()->getInput("core.move.left")),
|
||||
m_right(InputMap::get()->getInput("core.move.right")),
|
||||
m_up(InputMap::get()->getInput("core.move.up")),
|
||||
m_down(InputMap::get()->getInput("core.move.down"))
|
||||
{
|
||||
}
|
||||
|
||||
InputQueue::~InputQueue()
|
||||
{
|
||||
if (m_running)
|
||||
{
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
void InputQueue::run(std::chrono::milliseconds dt, client::Network* network)
|
||||
{
|
||||
using std::chrono::system_clock;
|
||||
system_clock::time_point next = system_clock::now();
|
||||
while (m_running)
|
||||
{
|
||||
m_sequence++;
|
||||
InputState state = getCurrentState();
|
||||
m_queue.push(state);
|
||||
network->sendState(state);
|
||||
next = next + dt;
|
||||
std::this_thread::sleep_until(next);
|
||||
}
|
||||
}
|
||||
|
||||
void InputQueue::start(std::chrono::milliseconds dt, client::Network* network)
|
||||
{
|
||||
m_running = true;
|
||||
std::thread thread1 = std::thread(&InputQueue::run, this, dt, network);
|
||||
std::swap(m_thread, thread1);
|
||||
}
|
||||
|
||||
void InputQueue::stop()
|
||||
{
|
||||
m_running = false;
|
||||
m_thread.join();
|
||||
}
|
||||
|
||||
InputState InputQueue::getCurrentState()
|
||||
{
|
||||
InputState input;
|
||||
input.forward = InputMap::get()->getState(m_forward);
|
||||
input.backward = InputMap::get()->getState(m_backward);
|
||||
input.left = InputMap::get()->getState(m_left);
|
||||
input.right = InputMap::get()->getState(m_right);
|
||||
input.up = InputMap::get()->getState(m_up);
|
||||
input.down = InputMap::get()->getState(m_down);
|
||||
input.rotation.x = static_cast<unsigned int>(
|
||||
m_registry->get<Position>(m_player->getEntity()).rotation.x * 360000.0);
|
||||
input.rotation.y = static_cast<unsigned int>(
|
||||
m_registry->get<Position>(m_player->getEntity()).rotation.y * 360000.0);
|
||||
input.sequence = m_sequence;
|
||||
return input;
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
// Copyright 2019-20 Genten Studios
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <Client/Network.hpp>
|
||||
|
||||
#include <Common/Logger.hpp>
|
||||
|
||||
using namespace phx::client;
|
||||
|
||||
Network::Network(std::ostringstream& chat) : m_chat(chat)
|
||||
{
|
||||
m_client = new phx::net::Host();
|
||||
|
||||
m_client->onReceive([this](phx::net::Peer& peer, phx::net::Packet&& packet,
|
||||
enet_uint32 channelID) {
|
||||
switch (channelID)
|
||||
{
|
||||
case 0:
|
||||
parseEvent(packet);
|
||||
break;
|
||||
case 1:
|
||||
parseState(packet);
|
||||
break;
|
||||
case 2:
|
||||
parseMessage(packet);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
m_client->onDisconnect([this](std::size_t peerID, enet_uint32) {
|
||||
std::cout << "Server disconnected";
|
||||
});
|
||||
|
||||
phx::net::Address address = phx::net::Address("127.0.0.1", 7777);
|
||||
m_client->connect(address, 3);
|
||||
m_client->poll(5000_ms);
|
||||
}
|
||||
|
||||
Network::~Network()
|
||||
{
|
||||
if (m_running)
|
||||
stop();
|
||||
delete m_client;
|
||||
}
|
||||
|
||||
void Network::run()
|
||||
{
|
||||
while (m_running)
|
||||
{
|
||||
m_client->poll();
|
||||
}
|
||||
}
|
||||
|
||||
void Network::start()
|
||||
{
|
||||
m_running = true;
|
||||
std::thread thread1 = std::thread(&Network::run, this);
|
||||
std::swap(m_thread, thread1);
|
||||
}
|
||||
|
||||
void Network::stop()
|
||||
{
|
||||
m_running = false;
|
||||
m_thread.join();
|
||||
}
|
||||
|
||||
void Network::parseEvent(phx::net::Packet& packet)
|
||||
{
|
||||
std::cout << "Event received";
|
||||
}
|
||||
|
||||
void Network::parseState(phx::net::Packet& packet) {}
|
||||
|
||||
void Network::parseMessage(phx::net::Packet& packet)
|
||||
{
|
||||
std::string input;
|
||||
|
||||
auto data = packet.getData();
|
||||
|
||||
phx::Serializer ser(Serializer::Mode::READ);
|
||||
ser.setBuffer(reinterpret_cast<std::byte*>(data.data()), data.size());
|
||||
ser& input;
|
||||
|
||||
LOG_INFO("Messenger") << input;
|
||||
m_chat << input;
|
||||
m_chat << "\n";
|
||||
}
|
||||
|
||||
void Network::sendState(InputState inputState)
|
||||
{
|
||||
Serializer ser(Serializer::Mode::WRITE);
|
||||
ser& inputState;
|
||||
auto state = ser.getBuffer();
|
||||
|
||||
phx::net::Packet packet =
|
||||
phx::net::Packet(ser.getBuffer(), phx::net::PacketFlags::UNRELIABLE);
|
||||
m_client->broadcast(packet, 1);
|
||||
}
|
||||
|
||||
void Network::sendMessage(std::string message)
|
||||
{
|
||||
Serializer ser(Serializer::Mode::WRITE);
|
||||
ser& message;
|
||||
phx::net::Packet packet =
|
||||
phx::net::Packet(ser.getBuffer(), phx::net::PacketFlags::RELIABLE);
|
||||
m_client->broadcast(packet, 2);
|
||||
}
|
|
@ -33,8 +33,22 @@
|
|||
* @copyright Copyright (c) 2019-2020 Genten Studios
|
||||
*/
|
||||
|
||||
namespace phx{
|
||||
struct Hand{
|
||||
voxels::BlockType* hand;
|
||||
};
|
||||
}
|
||||
#include <Common/Input.hpp>
|
||||
#include <Common/Voxels/Block.hpp>
|
||||
#include <entt/entt.hpp>
|
||||
|
||||
namespace phx
|
||||
{
|
||||
struct Hand
|
||||
{
|
||||
voxels::BlockType* hand;
|
||||
};
|
||||
|
||||
class ActorSystem
|
||||
{
|
||||
public:
|
||||
static entt::entity registerActor(entt::registry* registry);
|
||||
static void tick(entt::registry* registry, entt::entity entity,
|
||||
const float dt, InputState input);
|
||||
};
|
||||
} // namespace phx
|
|
@ -1,23 +1,28 @@
|
|||
add_subdirectory(Math)
|
||||
add_subdirectory(Voxels)
|
||||
add_subdirectory(CMS)
|
||||
add_subdirectory(Serialization)
|
||||
add_subdirectory(Network)
|
||||
|
||||
set(currentDir ${CMAKE_CURRENT_LIST_DIR})
|
||||
set(Headers
|
||||
${mathHeaders}
|
||||
${voxelHeaders}
|
||||
${cmsHeaders}
|
||||
${serializationHeaders}
|
||||
${networkHeaders}
|
||||
|
||||
${currentDir}/CoreIntrinsics.hpp
|
||||
${currentDir}/EnumTools.hpp
|
||||
${currentDir}/Singleton.hpp
|
||||
${currentDir}/FileIO.hpp
|
||||
${currentDir}/Logger.hpp
|
||||
${currentDir}/CoreIntrinsics.hpp
|
||||
${currentDir}/EnumTools.hpp
|
||||
${currentDir}/Singleton.hpp
|
||||
${currentDir}/FileIO.hpp
|
||||
${currentDir}/Logger.hpp
|
||||
|
||||
${currentDir}/Settings.hpp
|
||||
${currentDir}/Commander.hpp
|
||||
${currentDir}/Position.hpp
|
||||
${currentDir}/Movement.hpp
|
||||
${currentDir}/Settings.hpp
|
||||
${currentDir}/Commander.hpp
|
||||
${currentDir}/Position.hpp
|
||||
${currentDir}/Movement.hpp
|
||||
${currentDir}/Input.hpp
|
||||
|
||||
PARENT_SCOPE
|
||||
)
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
// Copyright 2019-20 Genten Studios
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Common/Math/Math.hpp>
|
||||
#include <Common/Serialization/Serializer.hpp>
|
||||
#include <cstddef>
|
||||
|
||||
namespace phx
|
||||
{
|
||||
struct InputState : public phx::ISerializable
|
||||
{
|
||||
bool forward{};
|
||||
bool backward{};
|
||||
bool left{};
|
||||
bool right{};
|
||||
bool up{};
|
||||
bool down{};
|
||||
math::vec2u rotation{}; // in 1/1000 degres
|
||||
std::size_t sequence{};
|
||||
Serializer& operator&(Serializer& this_) override;
|
||||
};
|
||||
} // namespace phx
|
|
@ -0,0 +1,155 @@
|
|||
// Copyright 2019-20 Genten Studios
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <enet/enet.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace phx::net
|
||||
{
|
||||
/**
|
||||
* @brief Represents an address to a Host or Peer.
|
||||
*
|
||||
* This class provides an abstraction to set an Address and Port to aid
|
||||
* connection through the Host class, as well as a way to identify a peer
|
||||
* (not uniquely).
|
||||
*
|
||||
* @paragraph Usage
|
||||
* @code
|
||||
* // this is for the server, you set a Port to listen on, but not an
|
||||
* // address to connect to, default address will be to listen on all
|
||||
* // bindable network interfaces.
|
||||
* Address address2(1234);
|
||||
* Host server(address2);
|
||||
*
|
||||
* // address to connect to.
|
||||
* Address address3("localhost", 1234);
|
||||
* Host client;
|
||||
* client.connect(address3);
|
||||
* @endcode
|
||||
*/
|
||||
class Address
|
||||
{
|
||||
public:
|
||||
Address() = default;
|
||||
|
||||
/**
|
||||
* @brief Creates an address to bind to.
|
||||
* @param port The port to listen on.
|
||||
*
|
||||
* The usage of this is mainly for something like a server application,
|
||||
* where there is not an address to connect or listen to, except all
|
||||
* bindable interfaces. Using this will allow a server to listen for
|
||||
* data on a specific port - this is useless for a client, since you can
|
||||
* just use the Host::connect method with the port and host address set
|
||||
* correctly.
|
||||
*/
|
||||
explicit Address(enet_uint16 port);
|
||||
|
||||
/**
|
||||
* @brief Constructs an address to connect to.
|
||||
* @param host The host that is going to be connected to.
|
||||
* @param port The port that will be connected to.
|
||||
*
|
||||
* The usage of this constructor is more for the client side, where it
|
||||
* connects to a peer, or rather a Server. This method is not the most
|
||||
* ideal since it would require previous encoding of an IP or hostname
|
||||
* to an unsigned integer. The other constructor requiring a string and
|
||||
* a port are more suited to external use.
|
||||
*/
|
||||
Address(enet_uint32 host, enet_uint16 port);
|
||||
|
||||
/**
|
||||
* @brief Constructs a user-friendly address to connect to.
|
||||
* @param host The hostname or IP that will be connected to.
|
||||
* @param port The port that will be connected to.
|
||||
*
|
||||
* This constructor is made for the user end of the Client connection
|
||||
* system. An IP such as 127.0.0.1 or 8.8.8.8 (Google) can be used, as
|
||||
* well as a domain name, such as google.com or similar. The port has
|
||||
* the same use. The pairing with Host::connect will allow a user to
|
||||
* connect to any server they wish.
|
||||
*/
|
||||
Address(const std::string& host, enet_uint16 port);
|
||||
|
||||
// copy constructor & assignment operator.
|
||||
Address(const ENetAddress& address);
|
||||
Address& operator=(const ENetAddress& address);
|
||||
|
||||
// move constructor & assignment operator.
|
||||
Address(ENetAddress&& address);
|
||||
Address& operator=(ENetAddress&& address);
|
||||
|
||||
/**
|
||||
* @brief Sets the host to another value/address.
|
||||
* @param host The hostname or IP that will be connected to.
|
||||
*
|
||||
* This should be preferred over the other setHost, with the signature
|
||||
* requiring an unsigned integer. That is inconvenient for use and only
|
||||
* exists for internal purposes.
|
||||
*/
|
||||
void setHost(const std::string& host);
|
||||
|
||||
/**
|
||||
* @brief Sets the host to another provided & pre-encoded value/address.
|
||||
* @param host The host that will be connected to.
|
||||
*/
|
||||
void setHost(enet_uint32 host);
|
||||
|
||||
/**
|
||||
* @brief Sets the port to the provided value.
|
||||
* @param port The port that will be connected to.
|
||||
*/
|
||||
void setPort(enet_uint16 port);
|
||||
|
||||
/**
|
||||
* @brief Gets the hostname that will be connected to.
|
||||
* @return The hostname of the address that will be connected to.
|
||||
*/
|
||||
std::string getHostname() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the IP that will be connected to.
|
||||
* @return The IP of the address that will be connected to.
|
||||
*/
|
||||
std::string getIP() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the port that will be connected to.
|
||||
* @return The port that will be connected to.
|
||||
*/
|
||||
enet_uint16 getPort() const;
|
||||
|
||||
operator const ENetAddress*() const { return &m_address; }
|
||||
|
||||
private:
|
||||
ENetAddress m_address;
|
||||
};
|
||||
} // namespace phx::net
|
|
@ -0,0 +1,10 @@
|
|||
set(currentDir ${CMAKE_CURRENT_LIST_DIR})
|
||||
set(networkHeaders
|
||||
${currentDir}/Types.hpp
|
||||
${currentDir}/Address.hpp
|
||||
${currentDir}/Peer.hpp
|
||||
${currentDir}/Packet.hpp
|
||||
${currentDir}/Host.hpp
|
||||
|
||||
PARENT_SCOPE
|
||||
)
|
|
@ -0,0 +1,339 @@
|
|||
// Copyright 2019-20 Genten Studios
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Common/Network/Address.hpp>
|
||||
#include <Common/Network/Packet.hpp>
|
||||
#include <Common/Network/Peer.hpp>
|
||||
#include <Common/Network/Types.hpp>
|
||||
|
||||
#include <enet/enet.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace phx::net
|
||||
{
|
||||
/**
|
||||
* @brief Represents a host, acting as either a server or a client.
|
||||
*
|
||||
* This class provides the functionality for an application to receive,
|
||||
* connect and send data to peers.
|
||||
*
|
||||
* The terminology of "channels" is a system where data can be separated
|
||||
* when sent and received. This allows for data to be handled separately,
|
||||
* removing the need for space-wasting headers - this is handled internally.
|
||||
*
|
||||
* A note to keep in mind is that the maximum number of peers possible is
|
||||
* 4096.
|
||||
*
|
||||
* @paragraph Usage
|
||||
* The constructor requiring only the amount of peers and address has been
|
||||
* setup so the defaults are correct for a client. The only requires a
|
||||
* single peer (the server) and the address is blank since it will listen to
|
||||
* all binding interfaces on the computer, for replies from the server.
|
||||
*
|
||||
* The other constructor is more ideal for a server, where an address -
|
||||
* which can be set so only the port is defined and all bindable interfaces
|
||||
* are listened to - and the amount of peers, alongside the number of
|
||||
* required channels is provided.
|
||||
*
|
||||
* @code
|
||||
* Host client;
|
||||
*
|
||||
* // set the server to listen on port 1234.
|
||||
* Host server(Address{1234});
|
||||
*
|
||||
* Address serverAddress = Address("127.0.0.1", 1234);
|
||||
* client.connect(serverAddress);
|
||||
*
|
||||
* server.onConnect([](Peer& peer, enet_uint32) { std::cout << "Client
|
||||
* connected from: " << peer.getAddress().getIP(); });
|
||||
*
|
||||
* server.onReceive([](Peer& peer, Packet&&, enet_uint32) { std::cout <<
|
||||
* "Received packet from: " << peer.getAddress().getIP(); });
|
||||
*
|
||||
* server.onDisconnect([](std::size_t peerID, enet_uint32) { std::cout <<
|
||||
* "Client disconnected: " << peerID; });
|
||||
*
|
||||
* client.onConnect([](Peer&, enet_uint32) { std::cout << "connected to
|
||||
* server"; });
|
||||
*
|
||||
* client.onReceive([](Peer&, Packet&&, enet_uint8) { std::cout <<
|
||||
* "received packet from server."; });
|
||||
*
|
||||
* client.onDisconnect([](std::size_t peerID, enet_uint32) { std::cout <<
|
||||
* "Disconnected from server"; });
|
||||
*
|
||||
* thread 1:
|
||||
* while (running) server.poll();
|
||||
*
|
||||
* thread 2:
|
||||
* while (running) client.poll();
|
||||
* @endcode
|
||||
*/
|
||||
class Host
|
||||
{
|
||||
public:
|
||||
using ReceiveCallback =
|
||||
std::function<void(Peer&, Packet&&, enet_uint32)>;
|
||||
using ConnectCallback = std::function<void(Peer&, enet_uint32)>;
|
||||
using DisconnectCallback =
|
||||
std::function<void(std::size_t, enet_uint32)>;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Creates a Host with the correct default values for a Client.
|
||||
* @param peers The amount of peers to allow connections from.
|
||||
* @param address The address to bind to.
|
||||
*
|
||||
* This constructor is ideal for the client - the defaults provided
|
||||
* allow for 1 peer (the server).
|
||||
*/
|
||||
Host(std::size_t peers = 1, const ENetAddress* address = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Creates a Host more suited to a server application.
|
||||
* @param address The address to bind to - for listening on.
|
||||
* @param peers The maximum amount of peers that are allowed to connect.
|
||||
* @param channels The maximum number of channels that can be used.
|
||||
*
|
||||
* The address can be an address constructed with only a port. This
|
||||
* allows for listening on the port on of all bindable interfaces.
|
||||
* Providing a host/IP will allow you to listen on a specific IP if for
|
||||
* example, a server has multiple IPs provided to it.
|
||||
*/
|
||||
Host(const Address& address, std::size_t peers,
|
||||
std::size_t channels = 0);
|
||||
|
||||
~Host();
|
||||
|
||||
// check if value exists before using.
|
||||
using OptionalPeer = std::optional<std::reference_wrapper<Peer>>;
|
||||
|
||||
/**
|
||||
* @brief Connects to a provided address.
|
||||
* @param address The address to connect to.
|
||||
* @return A peer inside an std::optional in case the connection fails.
|
||||
*
|
||||
* @paragraph Usage
|
||||
* You must call the Host::poll function once a connection request has
|
||||
* been created. The connection data will not be dispatched until poll
|
||||
* has been called, a recommended example of connecting is shown below:
|
||||
* @code
|
||||
* bool connected;
|
||||
* client.onConnect([&connected](Peer&, enet_uint32) { connected = true;
|
||||
* });
|
||||
*
|
||||
* Peer server;
|
||||
*
|
||||
* auto peer = client.connect(Address{ "127.0.0.1", 1234 });
|
||||
* if (peer)
|
||||
* {
|
||||
* // value for the optional, get for the reference_wrapper
|
||||
* server = peer.value().get();
|
||||
* }
|
||||
*
|
||||
* // poll for 5 seconds.
|
||||
* client.poll(5000_ms);
|
||||
*
|
||||
* if (connected == false)
|
||||
* {
|
||||
* // connection failed, try in a loop a few times if you want, but
|
||||
* // it's already been tried.
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
OptionalPeer connect(const Address& address);
|
||||
|
||||
/**
|
||||
* @brief Connects to a provided address.
|
||||
* @param address The address to connect to.
|
||||
* @param channels The amount of channels to enable on connect.
|
||||
* @param data User data to send, use only if understood.
|
||||
* @return A peer inside an std::optional in case the connection fails.
|
||||
*/
|
||||
OptionalPeer connect(const Address& address, enet_uint8 channels,
|
||||
enet_uint32 data = 0);
|
||||
|
||||
/**
|
||||
* @brief Gets the current bandwidth limits (incoming, outgoing).
|
||||
* @return The current bandwidth limits, in bytes per second.
|
||||
*/
|
||||
Bandwidth getBandwidthLimit() const;
|
||||
|
||||
/**
|
||||
* @brief Sets a new bandwidth limit.
|
||||
* @param bandwidth The bandwidth limits (incoming, outgoing) in bytes
|
||||
* per second.
|
||||
*/
|
||||
void setBandwidthLimit(const Bandwidth& bandwidth);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Gets the number of currently allocated channels.
|
||||
* @return The current number of allocated channels.
|
||||
*/
|
||||
std::size_t getChannelLimit() const;
|
||||
|
||||
/**
|
||||
* @brief Sets a new limit for the allocated channels.
|
||||
* @param limit A new limit for number of allocated channels.
|
||||
*/
|
||||
void setChannelLimit(std::size_t limit);
|
||||
|
||||
/**
|
||||
* @brief Broadcasts a packet to all connected peers.
|
||||
* @param packet The packet to send to all peers.
|
||||
* @param channel The channel to send it on.
|
||||
*/
|
||||
void broadcast(Packet& packet, enet_uint8 channel = 0);
|
||||
void broadcast(Packet&& packet, enet_uint8 channel = 0);
|
||||
|
||||
/**
|
||||
* @brief Sets a callback for when a packet is received.
|
||||
* @param callback The function to call when a packet is received.
|
||||
*/
|
||||
void onReceive(ReceiveCallback callback);
|
||||
|
||||
/**
|
||||
* @brief Sets a callback for when a new connection is established.
|
||||
* @param callback The function to call when a new peer is connected.
|
||||
*
|
||||
* Note: You don't get notified for your outgoing connections, like
|
||||
* clients to server, but when the server accepts the connection, you
|
||||
* get notified of a "request" - which is why you have to poll for
|
||||
* network events once a request has been created.
|
||||
*/
|
||||
void onConnect(ConnectCallback callback);
|
||||
|
||||
/**
|
||||
* @brief Sets a callback for when a peer is disconnected.
|
||||
* @param callback The function to call when a peer is disconnected.
|
||||
*
|
||||
* Note: You do get notified when a peer times out, etc...
|
||||
*/
|
||||
void onDisconnect(DisconnectCallback callback);
|
||||
|
||||
/**
|
||||
* @brief Polls for any network events that have occurred.
|
||||
* @param limit The limit of events that should be processed in this
|
||||
* call.
|
||||
*
|
||||
* Note: This function will not wait for events to occur, if there is
|
||||
* nothing in the queue immediately, this function will just end.
|
||||
*/
|
||||
void poll(int limit = 1);
|
||||
|
||||
/**
|
||||
* @brief Polls & waits for any network events that have occurred.
|
||||
* @param timeout How long to wait for an event for.
|
||||
* @param limit The maximum amount of events that should be processed.
|
||||
*/
|
||||
void poll(time::ms timeout, int limit = 1);
|
||||
|
||||
/**
|
||||
* @brief Flushes all events, sending them off to the respective foreign
|
||||
* hosts.
|
||||
*/
|
||||
void flush();
|
||||
|
||||
/**
|
||||
* @brief Gets the number of connected peers.
|
||||
* @return The number of connected peers.
|
||||
*/
|
||||
std::size_t getPeerCount() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the maximum number of possible connected peers.
|
||||
* @return The maximum amount of peers that there can be.
|
||||
*/
|
||||
std::size_t getPeerLimit() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the address of this host.
|
||||
* @return The address of this host.
|
||||
*
|
||||
* This is for the most part, fairly useless, but it can be used to get
|
||||
* what port is being used if that data is lost down the line.
|
||||
*/
|
||||
const Address& getAddress() const;
|
||||
|
||||
/**
|
||||
* @brief Gets a peer based on its ID.
|
||||
* @param id The ID of the Peer.
|
||||
* @return A pointer to the peer, or a nullptr if it doesn't exit.
|
||||
*/
|
||||
Peer* getPeer(std::size_t id);
|
||||
|
||||
/**
|
||||
* @brief Gets the total amount of data received.
|
||||
* @return The total amount of data received in bytes.
|
||||
*
|
||||
* Note: This value can overflow. Don't use if not needed.
|
||||
*/
|
||||
enet_uint32 getTotalReceievedData() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the total amount of data sent.
|
||||
* @return The total amount of data sent in bytes.
|
||||
*
|
||||
* Note: This value can overflow. Don't use if not needed.
|
||||
*/
|
||||
enet_uint32 getTotalSentData() const;
|
||||
|
||||
operator ENetHost*() const { return m_host; }
|
||||
|
||||
private:
|
||||
void handleEvent(ENetEvent& event);
|
||||
|
||||
Peer& getPeer(ENetPeer& peer);
|
||||
Peer& createPeer(ENetPeer& peer);
|
||||
void removePeer(ENetPeer& peer);
|
||||
|
||||
friend class Peer;
|
||||
void disconnectPeer(std::size_t id);
|
||||
void removePeer(const Peer& peer);
|
||||
|
||||
private:
|
||||
ENetHost* m_host;
|
||||
Address m_address;
|
||||
|
||||
ReceiveCallback m_receiveCallback;
|
||||
ConnectCallback m_connectCallback;
|
||||
DisconnectCallback m_disconnectCallback;
|
||||
|
||||
std::size_t m_peerID = 0;
|
||||
std::unordered_map<std::size_t, Peer> m_peers;
|
||||
|
||||
static std::atomic<std::size_t> m_activeInstances;
|
||||
};
|
||||
} // namespace phx::net
|
|
@ -0,0 +1,158 @@
|
|||
// Copyright 2019-20 Genten Studios
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Common/EnumTools.hpp>
|
||||
#include <Common/Network/Types.hpp>
|
||||
|
||||
#include <enet/enet.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
namespace phx::net
|
||||
{
|
||||
/**
|
||||
* @brief Flags for deciding Packet flow.
|
||||
*/
|
||||
enum class PacketFlags
|
||||
{
|
||||
/**
|
||||
* @brief The packet must use reliable delivery.
|
||||
*
|
||||
* A reliable packet is guaranteed to be delivered, and retries will be
|
||||
* attempted until an acknowledgement (ack) is received. If a certain
|
||||
* number of retries fail, a disconnection will be assumed and the
|
||||
* connection will be forcefully reset.
|
||||
*
|
||||
* Reliable delivery is slow, if a majority of reliable is required, use
|
||||
* a TCP library rather than ENet, which is designed with unreliable
|
||||
* delivery of UDP packets in mind.
|
||||
*/
|
||||
RELIABLE = ENET_PACKET_FLAG_RELIABLE,
|
||||
|
||||
/**
|
||||
* @brief The packet will be sent unreliably.
|
||||
*
|
||||
* No retry attempts will be made, nor will and acks be received.
|
||||
*/
|
||||
UNRELIABLE = ENET_PACKET_FLAG_SENT,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents a packet that will be sent to a host/peer.
|
||||
*/
|
||||
class Packet
|
||||
{
|
||||
public:
|
||||
using Data = std::vector<std::byte>;
|
||||
|
||||
public:
|
||||
Packet() = default;
|
||||
|
||||
/**
|
||||
* @brief Constructs a packet with some default data.
|
||||
* @param data The data to send within the packet.
|
||||
* @param flags The method with which the packet should be sent.
|
||||
*/
|
||||
Packet(const Data& data, PacketFlags flags);
|
||||
|
||||
/**
|
||||
* @brief Constructs a packet with a predetermined size.
|
||||
* @param size The size of the data which will be set later.
|
||||
* @param flags The method with which the packet should be sent.
|
||||
*/
|
||||
Packet(std::size_t size, PacketFlags flags);
|
||||
|
||||
// internal use.
|
||||
Packet(ENetPacket& packet, bool sent);
|
||||
|
||||
~Packet();
|
||||
|
||||
/**
|
||||
* @brief Sets the data the packet will have.
|
||||
* @param data The data to set within the packet.
|
||||
*
|
||||
* This method will implicitly resize the packet if required.
|
||||
*/
|
||||
void setData(const Data& data);
|
||||
Packet& operator=(const Data& data);
|
||||
|
||||
/// @todo Implement a stream (<<) operator for adding data.
|
||||
|
||||
/*
|
||||
* @brief Gets the data the packet is storing.
|
||||
* @return The packet's data.
|
||||
*
|
||||
* This method is useful on the receiving end. It will return a copy of
|
||||
* the data since the packet's lifetime cannot be determined.
|
||||
*/
|
||||
Data getData() const;
|
||||
|
||||
/**
|
||||
* @brief Resizes the packet.
|
||||
* @param size The new size for the packet.
|
||||
*/
|
||||
void resize(std::size_t size);
|
||||
|
||||
/**
|
||||
* @brief Gets the size of the packet.
|
||||
* @return The size of the packet.
|
||||
*/
|
||||
std::size_t getSize() const;
|
||||
|
||||
operator ENetPacket*() const { return m_packet; }
|
||||
|
||||
/**
|
||||
* @brief Prepares a packet for sending.
|
||||
*
|
||||
* Note: this currently doesn't do much but set a variable, but this
|
||||
* variable is important to guarantee that a packet is only destroyed
|
||||
* once ready. Without this, the destructor may prematurely delete the
|
||||
* packet's data.
|
||||
*/
|
||||
void prepareForSend();
|
||||
|
||||
/**
|
||||
* @brief Checks whether the packet has been sent.
|
||||
* @return Whether the packet has been sent or not.
|
||||
*/
|
||||
bool isSent() const { return m_sent; }
|
||||
|
||||
private:
|
||||
void create(const Data& data, PacketFlags flags);
|
||||
|
||||
private:
|
||||
ENetPacket* m_packet;
|
||||
bool m_sent = false;
|
||||
};
|
||||
} // namespace phx::net
|
||||
|
||||
ENABLE_BITWISE_OPERATORS(phx::net::PacketFlags);
|
|
@ -0,0 +1,235 @@
|
|||
// Copyright 2019-20 Genten Studios
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Common/Network/Address.hpp>
|
||||
#include <Common/Network/Packet.hpp>
|
||||
#include <Common/Network/Types.hpp>
|
||||
|
||||
#include <enet/enet.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
|
||||
namespace phx::net
|
||||
{
|
||||
enum class PeerStatus
|
||||
{
|
||||
CONNECTING = ENET_PEER_STATE_CONNECTING,
|
||||
CONNECTED = ENET_PEER_STATE_CONNECTED,
|
||||
DISCONNECTING = ENET_PEER_STATE_DISCONNECTING,
|
||||
DISCONNECTED = ENET_PEER_STATE_DISCONNECTED,
|
||||
};
|
||||
|
||||
class Host;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
// not for user use
|
||||
struct PeerData
|
||||
{
|
||||
void* data;
|
||||
std::size_t id;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* @brief Class to represent a Peer, a foreign host that has been connected
|
||||
* to.
|
||||
*
|
||||
* This class is always instantiated by the Host class, a user must not
|
||||
* construct a peer that is not default constructed. A default constructed
|
||||
* peer must still NOT be used, all methods are unsafe until set to a valid
|
||||
* Peer produced by a Host.
|
||||
*/
|
||||
class Peer
|
||||
{
|
||||
public:
|
||||
using Callback = std::function<void(const Packet&, enet_uint8)>;
|
||||
|
||||
public:
|
||||
Peer() = default;
|
||||
Peer(Host& host, ENetPeer& peer);
|
||||
|
||||
Peer& operator=(ENetPeer& peer);
|
||||
|
||||
/**
|
||||
* @brief Disconnects a peer from the host.
|
||||
* @param data Data to send to the foreign host, use only if understood.
|
||||
*
|
||||
* Note: Host::poll must be called if you want a disconnect event to
|
||||
* propagate. Use cases for this are the client disconnected it's peer
|
||||
* (the server). The client must poll and wait for a disconnect event
|
||||
* before quitting.
|
||||
*/
|
||||
void disconnect(enet_uint32 data = 0);
|
||||
|
||||
/**
|
||||
* @brief Disconnects a peer from the host IMMEDIATELY.
|
||||
* @param data Data to send to the foreign host, use only if understood.
|
||||
*
|
||||
* Note: This will not produce an event on the host calling this
|
||||
* function. For example, if the client disconnects it's peer (the
|
||||
* server) using this method, it will manually have to tell itself that
|
||||
* it has disconnected, it will not receive an event.
|
||||
*/
|
||||
void disconnectImmediately(enet_uint32 data = 0);
|
||||
|
||||
/**
|
||||
* @brief Disconnects a peer from the host once packets are sent.
|
||||
* @param data Data to send to the foreign host, use only if understood.
|
||||
*
|
||||
* This function essentially doesn't do much different from the
|
||||
* disconnect() method, however it will not read any events queued
|
||||
* locally for processing, it will send any packets left to send, and
|
||||
* then ditch any packets that may have been received.
|
||||
*/
|
||||
void disconnectOncePacketsAreSent(enet_uint32 data = 0);
|
||||
|
||||
/**
|
||||
* @brief Drops a peer entirely.
|
||||
*
|
||||
* Note: this function is not so different from disconnectImmediately().
|
||||
*/
|
||||
void drop();
|
||||
|
||||
/**
|
||||
* @brief Pings the host.
|
||||
*/
|
||||
void ping() const;
|
||||
|
||||
/**
|
||||
* @brief Gets how long between every automatic ping between peer/host.
|
||||
* @return The interval between pings.
|
||||
*/
|
||||
time::ms getPingInterval() const;
|
||||
|
||||
/**
|
||||
* @brief Sets how long between every automatic ping between peer/host.
|
||||
* @param interval The interval at which to ping the host.
|
||||
*/
|
||||
void setPingInterval(time::ms interval);
|
||||
|
||||
/**
|
||||
* @brief Gets how long it takes for data to go somewhere and come back.
|
||||
* @return The round trip time, from the peer to the host to the peer.
|
||||
*/
|
||||
time::ms getRoundTripTime() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the ratio of packet loss.
|
||||
* @return The ratio of packet loss.
|
||||
*/
|
||||
enet_uint32 getPacketLoss() const;
|
||||
|
||||
/**
|
||||
* @brief Waits to receive a packet from a peer.
|
||||
* @param callback The callback to use when a packet is received.
|
||||
*
|
||||
* Note: this function is useful if you want to filter a peer to receive
|
||||
* a packet from, useful in the cases of authentication or waiting for
|
||||
* something.
|
||||
*/
|
||||
void receive(Callback callback) const;
|
||||
|
||||
/**
|
||||
* @brief Sends a packet to the peer.
|
||||
* @param packet The packet to send to the peer.
|
||||
* @param channel The channel to send it on.
|
||||
*/
|
||||
void send(Packet& packet, enet_uint8 channel = 0);
|
||||
void send(Packet&& packet, enet_uint8 channel = 0);
|
||||
|
||||
/**
|
||||
* @brief Gets the current throttle parameters.
|
||||
* @return The current throttle parameters.
|
||||
*
|
||||
* To understand more about how the throttle works:
|
||||
* http://enet.bespin.org/group__peer.html#gab35807c848b6c88af12ce8476dffbc84
|
||||
*/
|
||||
Throttle getThrottle() const;
|
||||
|
||||
/**
|
||||
* @brief Sets new throttle parameters.
|
||||
* @param throttle The new throttle parameters.
|
||||
*
|
||||
* To understand more about how the throttle works:
|
||||
* http://enet.bespin.org/group__peer.html#gab35807c848b6c88af12ce8476dffbc84
|
||||
*/
|
||||
void setThrottle(const Throttle& throttle);
|
||||
|
||||
/**
|
||||
* @brief Gets the current timeout parameters.
|
||||
* @return The current parameters for when a timeout event is issued.
|
||||
*
|
||||
* Note: The timeout is how long the host will wait before receiving
|
||||
* confirmation on connection before issuing a disconnect event.
|
||||
*/
|
||||
Timeout getTimeout() const;
|
||||
|
||||
/**
|
||||
* @brief Sets new timeout parameters.
|
||||
* @param timeout The new parameters for when a timeout event is issued.
|
||||
*
|
||||
* Note: The timeout is how long the host will wait before receiving
|
||||
* confirmation on connection before issuing a disconnect event.
|
||||
*/
|
||||
void setTimeout(const Timeout& timeout);
|
||||
|
||||
/**
|
||||
* @brief Gets the address of the peer.
|
||||
* @return The address of the peer.
|
||||
*/
|
||||
const Address& getAddress() const;
|
||||
|
||||
/**
|
||||
* @brief The current state of the peer.
|
||||
* @return Gets the current state of the peer.
|
||||
*/
|
||||
PeerStatus getState() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the peer's unique ID.
|
||||
* @return The unique ID allocated to the peer.
|
||||
*
|
||||
* Note: This ID will always be unique, regardless of previous peers -
|
||||
* this can be used to uniquely identify a peer with a user or something
|
||||
* similar.
|
||||
*/
|
||||
std::size_t getID() const { return std::size_t(m_peer->data); }
|
||||
|
||||
operator ENetPeer*() const { return m_peer; }
|
||||
|
||||
private:
|
||||
ENetPeer* m_peer;
|
||||
|
||||
Host* m_host;
|
||||
Address m_address;
|
||||
};
|
||||
} // namespace phx::net
|
|
@ -0,0 +1,74 @@
|
|||
// Copyright 2019-20 Genten Studios
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <enet/enet.h>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace phx
|
||||
{
|
||||
namespace time
|
||||
{
|
||||
// milliseconds
|
||||
using ms = std::chrono::duration<unsigned int, std::milli>;
|
||||
} // namespace time
|
||||
|
||||
namespace net
|
||||
{
|
||||
struct Timeout
|
||||
{
|
||||
time::ms limit;
|
||||
time::ms minimum;
|
||||
time::ms maximum;
|
||||
};
|
||||
|
||||
struct Throttle
|
||||
{
|
||||
time::ms interval;
|
||||
enet_uint32 acceleration;
|
||||
enet_uint32 deceleration;
|
||||
};
|
||||
|
||||
using speed = enet_uint32;
|
||||
|
||||
struct Bandwidth
|
||||
{
|
||||
// in bytes.
|
||||
speed incoming;
|
||||
speed outgoing;
|
||||
};
|
||||
} // namespace net
|
||||
} // namespace phx
|
||||
|
||||
// cool postfix operator, you can do 1000_ms instead of time::ms{1000}!
|
||||
constexpr phx::time::ms operator"" _ms(unsigned long long ms)
|
||||
{
|
||||
return phx::time::ms {ms};
|
||||
}
|
|
@ -32,14 +32,21 @@
|
|||
|
||||
namespace phx
|
||||
{
|
||||
/**
|
||||
* @brief The positioning for an entity
|
||||
*/
|
||||
struct Position
|
||||
{
|
||||
/// @brief The direction the entity is facing
|
||||
math::vec3 rotation;
|
||||
/// @brief The cardinal position of the entity
|
||||
math::vec3 position;
|
||||
};
|
||||
/**
|
||||
* @brief The positioning for an entity
|
||||
*/
|
||||
struct Position
|
||||
{
|
||||
/// @brief The direction the entity is facing
|
||||
math::vec3 rotation;
|
||||
/// @brief The cardinal position of the entity
|
||||
math::vec3 position;
|
||||
|
||||
math::vec3 getDirection()
|
||||
{
|
||||
return math::vec3 {std::cos(rotation.y) * std::sin(rotation.x),
|
||||
std::sin(rotation.y),
|
||||
std::cos(rotation.y) * std::cos(rotation.x)};
|
||||
};
|
||||
};
|
||||
} // namespace phx
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
set(currentDir ${CMAKE_CURRENT_LIST_DIR})
|
||||
set(serializationHeaders
|
||||
${currentDir}/SharedTypes.hpp
|
||||
${currentDir}/Endian.hpp
|
||||
${currentDir}/Serializer.hpp
|
||||
${currentDir}/Serializer.inl
|
||||
|
||||
PARENT_SCOPE
|
||||
)
|
|
@ -0,0 +1,284 @@
|
|||
// Copyright 2019-20 Genten Studios
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Common/CoreIntrinsics.hpp>
|
||||
#include <Common/Serialization/SharedTypes.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
// for potential GCC defines.
|
||||
#include <limits.h>
|
||||
|
||||
// by the end of this preprocessor cluster fuck, there should be an
|
||||
// ENGINE_NATIVE_ENDIAN that is defined. The static assert in the namespace will
|
||||
// make sure it's not ENGINE_UNKNOWN_ENDIAN, since if we cannot determine the
|
||||
// platform's endianness we do be in a bit of a pickle.
|
||||
|
||||
#define ENGINE_UNKNOWN_ENDIAN -1
|
||||
#define ENGINE_LITTLE_ENDIAN 0
|
||||
#define ENGINE_BIG_ENDIAN 1
|
||||
#define ENGINE_NET_ENDIAN ENGINE_BIG_ENDIAN
|
||||
|
||||
// c++17 feature, C++11 extension in CLang, GCC 5+, VS2015+
|
||||
// you should be fine even without it.
|
||||
#ifdef __has_include
|
||||
# if __has_include(<endian.h>)
|
||||
# include <endian.h> // gnu libc normally provides, linux
|
||||
# elif __has_include(<machine/endian.h>)
|
||||
# include <machine/endian.h> //open bsd, macos
|
||||
# elif __has_include(<sys/param.h>)
|
||||
# include <sys/param.h> // mingw, some bsd (not open/macos)
|
||||
# elif __has_include(<sys/isadefs.h>)
|
||||
# include <sys/isadefs.h> // solaris
|
||||
# endif
|
||||
#endif
|
||||
#if defined(__BIG_ENDIAN__)
|
||||
# define ENGINE_NATIVE_ENDIAN ENGINE_BIG_ENDIAN
|
||||
#elif defined(__LITTLE_ENDIAN__)
|
||||
# define ENGINE_NATIVE_ENDIAN ENGINE_LITTLE_ENDIAN
|
||||
#endif
|
||||
|
||||
// this should exhaust literally every possibility of byte order
|
||||
#if !defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)
|
||||
# if (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) || \
|
||||
(defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN) || \
|
||||
(defined(_BYTE_ORDER) && _BYTE_ORDER == _BIG_ENDIAN) || \
|
||||
(defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN) || \
|
||||
(defined(__sun) && defined(__SVR4) && defined(_BIG_ENDIAN)) || \
|
||||
defined(__ARMEB__) || defined(__THUMBEB__) || \
|
||||
defined(__AARCH64EB__) || defined(_MIBSEB) || defined(__MIBSEB) || \
|
||||
defined(__MIBSEB__) || defined(_M_PPC)
|
||||
# define ENGINE_NATIVE_ENDIAN ENGINE_BIG_ENDIAN
|
||||
# elif (defined(__BYTE_ORDER__) && \
|
||||
__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || /* gcc */ \
|
||||
(defined(__BYTE_ORDER) && \
|
||||
__BYTE_ORDER == __LITTLE_ENDIAN) /* linux header */ \
|
||||
|| (defined(_BYTE_ORDER) && _BYTE_ORDER == _LITTLE_ENDIAN) || \
|
||||
(defined(BYTE_ORDER) && \
|
||||
BYTE_ORDER == LITTLE_ENDIAN) /* mingw header */ \
|
||||
|| (defined(__sun) && defined(__SVR4) && \
|
||||
defined(_LITTLE_ENDIAN)) || /* solaris */ \
|
||||
defined(__ARMEL__) || \
|
||||
defined(__THUMBEL__) || defined(__AARCH64EL__) || defined(_MIPSEL) || \
|
||||
defined(__MIPSEL) || defined(__MIPSEL__) || defined(_M_IX86) || \
|
||||
defined(_M_X64) || defined(_M_IA64) || /* msvc for intel processors */ \
|
||||
defined(_M_ARM) /* msvc code on arm executes in little endian mode */
|
||||
# define ENGINE_NATIVE_ENDIAN ENGINE_LITTLE_ENDIAN
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// something is wrong, it hasn't been defined yet.
|
||||
#ifndef ENGINE_NATIVE_ENDIAN
|
||||
# define ENGINE_NATIVE_ENDIAN ENGINE_UNKNOWN_ENDIAN
|
||||
# warning \
|
||||
"Unknown platform endianness. Network/Host byte swaps will not occur - bugs may be present."
|
||||
#endif
|
||||
|
||||
// define builtin byte swapping methods, much faster than custom methods.
|
||||
// will work for signed as well.
|
||||
// clang-format off
|
||||
#if defined(ENGINE_MSVC)
|
||||
# define byteswap16(x) _byteswap_ushort((x))
|
||||
# define byteswap32(x) _byteswap_ulong((x))
|
||||
# define byteswap64(x) _byteswap_uint64((x))
|
||||
#elif defined(ENGINE_GNUC) && \
|
||||
((__GNUC__ >= 5) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
|
||||
# define byteswap16(x) __builtin_bswap16((x))
|
||||
# define byteswap32(x) __builtin_bswap32((x))
|
||||
# define byteswap64(x) __builtin_bswap64((x))
|
||||
#elif defined(__has_builtin) && __has_builtin(__builtin_bswap64)
|
||||
# define byteswap16(x) __builtin_bswap16((x))
|
||||
# define byteswap32(x) __builtin_bswap32((x))
|
||||
# define byteswap64(x) __builtin_bswap64((x))
|
||||
#else
|
||||
// compilers will most likely optimise this out - even if the builtin's don't
|
||||
// exist.
|
||||
static inline uint16_t byteswap16(uint16_t x)
|
||||
{
|
||||
return (((x >> 8) & 0xffu) | ((x & 0xffu) << 8));
|
||||
}
|
||||
static inline uint32_t byteswap32(uint32_t x)
|
||||
{
|
||||
return (((x & 0xff000000u) >> 24) | ((x & 0x00ff0000u) >> 8) |
|
||||
((x & 0x0000ff00u) << 8) | ((x & 0x000000ffu) << 24));
|
||||
}
|
||||
static inline uint64_t byteswap64(uint64_t x)
|
||||
{
|
||||
return (((x & 0xff00000000000000ull) >> 56) |
|
||||
((x & 0x00ff000000000000ull) >> 40) |
|
||||
((x & 0x0000ff0000000000ull) >> 24) |
|
||||
((x & 0x000000ff00000000ull) >> 8) |
|
||||
((x & 0x00000000ff000000ull) << 8) |
|
||||
((x & 0x0000000000ff0000ull) << 24) |
|
||||
((x & 0x000000000000ff00ull) << 40) |
|
||||
((x & 0x00000000000000ffull) << 56));
|
||||
}
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace phx::data::endian
|
||||
{
|
||||
/**
|
||||
* @brief Represents the different Endianness types.
|
||||
*/
|
||||
enum class Endian
|
||||
{
|
||||
LITTLE = ENGINE_LITTLE_ENDIAN,
|
||||
BIG = ENGINE_BIG_ENDIAN,
|
||||
|
||||
// The de-facto for networking endianness is big endian.
|
||||
NET = ENGINE_NET_ENDIAN,
|
||||
|
||||
// This is the native endianness of the platform, the huge clusterfuck
|
||||
// of a preprocessor if statement up above is how we figure this out.
|
||||
NATIVE = ENGINE_NATIVE_ENDIAN
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
// struct for internal use dictating whether a piece of data can be
|
||||
// changed in endianness. this is basically, an PoD and Floating Points.
|
||||
template <typename T>
|
||||
struct IsEndianChangable
|
||||
{
|
||||
static constexpr bool value = std::is_integral_v<T> || std::is_floating_point_v<T>;
|
||||
};
|
||||
|
||||
// handy template specialized way of handling byteswapping, so we don't
|
||||
// have to manually write code within the netToHost and hostToNet
|
||||
// functions at the bottom of this file.
|
||||
template <std::size_t N>
|
||||
struct ByteSwapper
|
||||
{
|
||||
};
|
||||
|
||||
// you don't swap the byte order if there's only one byte.
|
||||
template <>
|
||||
struct ByteSwapper<1>
|
||||
{
|
||||
template <typename T>
|
||||
T operator()(const T& t)
|
||||
{
|
||||
return t;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ByteSwapper<2>
|
||||
{
|
||||
template <typename T>
|
||||
T operator()(const T& t)
|
||||
{
|
||||
return byteswap16(t);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ByteSwapper<4>
|
||||
{
|
||||
template <typename T>
|
||||
T operator()(const T& t)
|
||||
{
|
||||
return byteswap32(t);
|
||||
}
|
||||
|
||||
// special operator for float since it needs to be turned into an
|
||||
// integer first.
|
||||
float operator()(float f)
|
||||
{
|
||||
uint64_t t = byteswap32(*reinterpret_cast<const uint32_t*>(&f));
|
||||
return *reinterpret_cast<const float*>(&t);
|
||||
}
|
||||
};
|
||||
|
||||
// anything bigger than 8 is bigger than 64bits and if we're there we've
|
||||
// fucked up already.
|
||||
template <>
|
||||
struct ByteSwapper<8>
|
||||
{
|
||||
template <typename T>
|
||||
T operator()(const T& t)
|
||||
{
|
||||
return byteswap64(t);
|
||||
}
|
||||
|
||||
// special operator for double since it needs to be turned into an
|
||||
// integer first.
|
||||
double operator()(double d)
|
||||
{
|
||||
uint64_t t = byteswap64(*reinterpret_cast<const uint64_t*>(&d));
|
||||
return *reinterpret_cast<const double*>(&t);
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* @brief Swaps the endianness of data for sending over a network.
|
||||
* @tparam T The type of data being converted. (must be integral type)
|
||||
* @tparam from The endianness that the data currently is.
|
||||
* @param t The value to check/swap for endianness
|
||||
* @return The endian-safe value.
|
||||
*/
|
||||
template <typename T, Endian from = Endian::NATIVE,
|
||||
typename U =
|
||||
std::enable_if_t<detail::IsEndianChangable<T>::value, void>>
|
||||
T swapForNetwork(const T& t)
|
||||
{
|
||||
if constexpr (from != Endian::NET)
|
||||
{
|
||||
return detail::ByteSwapper<sizeof(T)>()(t);
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Swaps the endianness of data after receiving from a network.
|
||||
* @tparam T The type of data being converted. (must be integral type)
|
||||
* @tparam from The endianness that the data currently is.
|
||||
* @param t The value to check/swap for endianness
|
||||
* @return The endian-safe value.
|
||||
*/
|
||||
template <typename T, Endian from = Endian::NET,
|
||||
typename U =
|
||||
std::enable_if_t<detail::IsEndianChangable<T>::value, void>>
|
||||
T swapForHost(const T& t)
|
||||
{
|
||||
if constexpr (from != Endian::NATIVE)
|
||||
{
|
||||
return detail::ByteSwapper<sizeof(T)>()(t);
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
} // namespace phx::data::endian
|
|
@ -0,0 +1,185 @@
|
|||
// Copyright 2019-20 Genten Studios
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Common/Serialization/Endian.hpp>
|
||||
#include <Common/Serialization/SharedTypes.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
|
||||
#if defined(__APPLE__)
|
||||
# define __INT32_EQUAL_LONG__ 1
|
||||
#elif defined(_WIN32)
|
||||
# define __INT32_EQUAL_LONG__ 1
|
||||
#else
|
||||
# define __INT32_EQUAL_LONG__ 0
|
||||
#endif
|
||||
|
||||
namespace phx
|
||||
{
|
||||
class Serializer;
|
||||
|
||||
/**
|
||||
* @brief Interface class for helping with data structures.
|
||||
*
|
||||
* This function must be overridden, usage for the Serializer can be found
|
||||
* below.
|
||||
*/
|
||||
class ISerializable
|
||||
{
|
||||
public:
|
||||
virtual Serializer& operator&(Serializer& serializer) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Serializes data with Endianness correction for network transfer.
|
||||
*
|
||||
* This class provides a way to safely accumulate data with automatic
|
||||
* correction for system endianness for transfer over a network. You can use
|
||||
* this class on both sides of the system.
|
||||
*
|
||||
* @paragraph Usage
|
||||
* If preparing data to send, the mode used must be Mode::WRITE, since
|
||||
* you're writing to the buffer. If you've just received data and funneled
|
||||
* the Packet's data into the serializer, you should use the Mode::READ
|
||||
* mode. This will prevent you from writing to the buffer (and vice versa
|
||||
* for the write mode).
|
||||
*
|
||||
* To pack a value into the buffer, use the & operator and write mode. The &
|
||||
* operator is small so you can have a chain of things like: ``serializer &
|
||||
* var & var2 & var3;``. The buffer can then be retrieved using ``getBuffer()``
|
||||
*
|
||||
* To retrieve a value from the buffer, again, use the & operator and read
|
||||
* mode. You should read **in the same direction of variables than when
|
||||
* you packed the buffer**. For example, pack the buffer like this:
|
||||
* @code
|
||||
* Serializer ser(Serializer::Mode::WRITE);
|
||||
* ser & var1 & var2 & var3 & var4;
|
||||
* @endcode
|
||||
* And unpack the buffer like this:
|
||||
* @code
|
||||
* Serializer ser(Serializer::Mode::READ);
|
||||
* ser & var1 & var2 & var3 & var4;
|
||||
* @endcode
|
||||
*
|
||||
* Usage with a packet:
|
||||
* @code
|
||||
* // client:
|
||||
* int status = 5;
|
||||
* bool moving = true;
|
||||
* float wowee = 0.01f;
|
||||
* std::size_t sequence = 100355;
|
||||
*
|
||||
* Serializer ser(Serializer::Mode::WRITE)
|
||||
* ser & status & moving & wowee & sequence;
|
||||
*
|
||||
* send_packet(ser.getBuffer());
|
||||
*
|
||||
* // server:
|
||||
* int status;
|
||||
* bool moving;
|
||||
* float wowee;
|
||||
* std::size_t sequence;
|
||||
*
|
||||
* Packet packet = receive_packet();
|
||||
*
|
||||
* Serializer ser(Serializer::Mode::READ)
|
||||
* ser.setBuffer(packet.getData());
|
||||
* ser & status & moving & wowee & sequence;
|
||||
*
|
||||
* // status, moving, wowee and sequence will be equal to their client
|
||||
* // counterparts.
|
||||
* @endcode
|
||||
*/
|
||||
class Serializer
|
||||
{
|
||||
public:
|
||||
enum class Mode
|
||||
{
|
||||
READ,
|
||||
WRITE
|
||||
};
|
||||
|
||||
public:
|
||||
explicit Serializer(Mode mode) : m_mode(mode) {}
|
||||
|
||||
data::Data& getBuffer() { return m_buffer; }
|
||||
void setBuffer(std::byte* data, std::size_t dataLength);
|
||||
void setBuffer(const data::Data& data) { m_buffer = data; }
|
||||
|
||||
Serializer& operator&(bool& value);
|
||||
Serializer& operator&(char& value);
|
||||
Serializer& operator&(unsigned char& value);
|
||||
Serializer& operator&(float& value);
|
||||
Serializer& operator&(double& value);
|
||||
|
||||
#if __INT32_EQUAL_LONG__
|
||||
Serializer& operator&(long& value);
|
||||
Serializer& operator&(unsigned long& value);
|
||||
#endif
|
||||
|
||||
Serializer& operator&(std::int16_t& value);
|
||||
Serializer& operator&(std::int32_t& value);
|
||||
Serializer& operator&(std::int64_t& value);
|
||||
Serializer& operator&(std::uint16_t& value);
|
||||
Serializer& operator&(std::uint32_t& value);
|
||||
Serializer& operator&(std::uint64_t& value);
|
||||
|
||||
template <typename T>
|
||||
Serializer& operator&(std::basic_string<T>& value);
|
||||
|
||||
Serializer& operator&(ISerializable& value);
|
||||
|
||||
static data::Data end(Serializer& serializer);
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
void push(const T& data);
|
||||
|
||||
template <typename T>
|
||||
void push(const std::basic_string<T>& data);
|
||||
|
||||
void push(ISerializable& data);
|
||||
|
||||
template <typename T>
|
||||
void pop(T& data);
|
||||
|
||||
template <typename T>
|
||||
void pop(std::basic_string<T>& data);
|
||||
|
||||
private:
|
||||
Mode m_mode;
|
||||
data::Data m_buffer;
|
||||
};
|
||||
} // namespace phx::data
|
||||
|
||||
#include <Common/Serialization/Serializer.inl>
|
|
@ -0,0 +1,348 @@
|
|||
// Copyright 2019-20 Genten Studios
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
namespace phx
|
||||
{
|
||||
inline void Serializer::setBuffer(std::byte* data, std::size_t dataLength)
|
||||
{
|
||||
m_buffer.clear();
|
||||
m_buffer.insert(m_buffer.begin(), data, data + dataLength);
|
||||
}
|
||||
|
||||
inline Serializer& Serializer::operator&(bool& value)
|
||||
{
|
||||
if (m_mode == Mode::READ)
|
||||
{
|
||||
pop(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
push(value);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Serializer& Serializer::operator&(char& value)
|
||||
{
|
||||
if (m_mode == Mode::READ)
|
||||
{
|
||||
pop(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
push(value);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Serializer& Serializer::operator&(unsigned char& value)
|
||||
{
|
||||
if (m_mode == Mode::READ)
|
||||
{
|
||||
pop(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
push(value);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Serializer& Serializer::operator&(float& value)
|
||||
{
|
||||
if (m_mode == Mode::READ)
|
||||
{
|
||||
pop(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
push(value);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Serializer& Serializer::operator&(double& value)
|
||||
{
|
||||
if (m_mode == Mode::READ)
|
||||
{
|
||||
pop(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
push(value);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if __INT32_EQUAL_LONG__
|
||||
inline Serializer& Serializer::operator&(long& value)
|
||||
{
|
||||
if (m_mode == Mode::READ)
|
||||
{
|
||||
pop(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
push(value);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Serializer& Serializer::operator&(unsigned long& value)
|
||||
{
|
||||
if (m_mode == Mode::READ)
|
||||
{
|
||||
pop(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
push(value);
|
||||
}
|
||||
return *this;
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
inline Serializer& Serializer::operator&(std::int16_t& value)
|
||||
{
|
||||
if (m_mode == Mode::READ)
|
||||
{
|
||||
pop(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
push(value);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Serializer& Serializer::operator&(std::int32_t& value)
|
||||
{
|
||||
if (m_mode == Mode::READ)
|
||||
{
|
||||
pop(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
push(value);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Serializer& Serializer::operator&(std::int64_t& value)
|
||||
{
|
||||
if (m_mode == Mode::READ)
|
||||
{
|
||||
pop(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
push(value);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Serializer& Serializer::operator&(std::uint16_t& value)
|
||||
{
|
||||
if (m_mode == Mode::READ)
|
||||
{
|
||||
pop(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
push(value);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Serializer& Serializer::operator&(std::uint32_t& value)
|
||||
{
|
||||
if (m_mode == Mode::READ)
|
||||
{
|
||||
pop(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
push(value);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Serializer& Serializer::operator&(std::uint64_t& value)
|
||||
{
|
||||
if (m_mode == Mode::READ)
|
||||
{
|
||||
pop(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
push(value);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline Serializer& Serializer::operator&(std::basic_string<T>& value)
|
||||
{
|
||||
if (m_mode == Mode::READ)
|
||||
{
|
||||
pop(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
push(value);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Serializer& Serializer::operator&(ISerializable& value)
|
||||
{
|
||||
return value & *this;
|
||||
}
|
||||
|
||||
|
||||
inline data::Data Serializer::end(Serializer& serializer)
|
||||
{
|
||||
return serializer.m_buffer;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
void Serializer::push(const T& data)
|
||||
{
|
||||
union {
|
||||
std::byte bytes[sizeof(T)];
|
||||
T value;
|
||||
} value;
|
||||
|
||||
value.value = data::endian::swapForNetwork(data);
|
||||
|
||||
for (std::size_t i = 0; i < sizeof(T); ++i)
|
||||
{
|
||||
m_buffer.push_back(value.bytes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Serializer::push(const std::basic_string<T>& data)
|
||||
{
|
||||
// if T is the same as the number of bits in a char, it's a normal
|
||||
// std::string. This means that there is only 1 byte per character and
|
||||
// so you don't need to factor in any endianness changes.
|
||||
if constexpr (sizeof(T) == CHAR_BIT)
|
||||
{
|
||||
// this is faster than iterating through every character and
|
||||
// swapping endianness and essentially doing an unnecessary
|
||||
// endianness swap.
|
||||
|
||||
// push size of string onto data at the end.
|
||||
// specify unsigned int otherwise it will waste space allocating a
|
||||
// 64 bit variable.
|
||||
push(static_cast<unsigned int>(data.length()));
|
||||
|
||||
// previous end of the array, so we can append onto that - rather
|
||||
// than the new end.
|
||||
const std::size_t prevEnd = m_buffer.size();
|
||||
|
||||
// +1 for null terminator.
|
||||
m_buffer.resize(m_buffer.size() + data.length() + 1);
|
||||
|
||||
// convert string to array of std::byte and append to data array.
|
||||
std::transform(data.begin(), data.end(), m_buffer.begin() + prevEnd,
|
||||
[](char c) { return std::byte(c); });
|
||||
}
|
||||
else
|
||||
{
|
||||
// push a new value for each character in the string.
|
||||
// the reason this exists is because there are different strings in
|
||||
// the standard library, them being 16bit and 32bit character
|
||||
// strings.
|
||||
push(static_cast<unsigned int>(data.length()));
|
||||
for (auto c : data)
|
||||
{
|
||||
push(c);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
inline void Serializer::push(ISerializable& data) { data&* this; }
|
||||
|
||||
template <typename T>
|
||||
void Serializer::pop(T& data)
|
||||
{
|
||||
union {
|
||||
std::byte bytes[sizeof(T)];
|
||||
T value;
|
||||
} value;
|
||||
|
||||
std::memcpy(value.bytes, m_buffer.data(), sizeof(T));
|
||||
|
||||
// basically pop front for the amount of bytes of data we're taking.
|
||||
m_buffer.erase(m_buffer.begin(), m_buffer.begin() + sizeof(T));
|
||||
|
||||
data = data::endian::swapForHost(value.value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Serializer::pop(std::basic_string<T>& data)
|
||||
{
|
||||
if constexpr (sizeof(T) == CHAR_BIT)
|
||||
{
|
||||
unsigned int size;
|
||||
pop(size);
|
||||
|
||||
data.resize(size);
|
||||
|
||||
std::transform(m_buffer.begin(), m_buffer.begin() + size,
|
||||
data.begin(),
|
||||
[](std::byte byte) { return char(byte); });
|
||||
|
||||
// basically pop front for the amount of bytes of data we're taking.
|
||||
m_buffer.erase(m_buffer.begin(), m_buffer.begin() + size);
|
||||
}
|
||||
else
|
||||
{
|
||||
union {
|
||||
std::byte bytes[sizeof(T)];
|
||||
T c;
|
||||
} values;
|
||||
|
||||
unsigned int size;
|
||||
pop(size);
|
||||
|
||||
data.reserve(size);
|
||||
|
||||
for (unsigned int i = 0; i < size; ++i)
|
||||
{
|
||||
pop(values.c);
|
||||
data.push_back(values.c);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace phx
|
|
@ -0,0 +1,38 @@
|
|||
// Copyright 2019-20 Genten Studios
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
namespace phx::data
|
||||
{
|
||||
using Data = std::vector<std::byte>;
|
||||
using Byte = std::byte;
|
||||
}
|
|
@ -0,0 +1,287 @@
|
|||
// Copyright 2019-20 Genten Studios
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
|
||||
#include <Common/Util/Front.hpp>
|
||||
|
||||
namespace phx
|
||||
{
|
||||
namespace
|
||||
{
|
||||
template <typename T>
|
||||
class IsValid
|
||||
{
|
||||
public:
|
||||
bool operator()(const T&) { return true; }
|
||||
};
|
||||
} // namespace
|
||||
|
||||
/**
|
||||
* @brief A thread safe queue
|
||||
*
|
||||
* @tparam T The type of object stored in the queue
|
||||
* @tparam Validator
|
||||
* @tparam Container
|
||||
*/
|
||||
template <typename T, typename Validator = IsValid<T>,
|
||||
typename Container = std::queue<T>>
|
||||
class BlockingQueue : public Container
|
||||
{
|
||||
public:
|
||||
virtual ~BlockingQueue() { stop(); }
|
||||
|
||||
void stop()
|
||||
{
|
||||
m_done = true;
|
||||
m_cond.notify_all();
|
||||
m_mutex.lock();
|
||||
m_mutex.unlock();
|
||||
}
|
||||
void clear()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
Container::c.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief checks if the queue is empty
|
||||
* @return true if empty
|
||||
* @return false if not empty
|
||||
*/
|
||||
bool empty() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
if (m_done)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool res = Container::empty();
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the size of the queue
|
||||
*
|
||||
* @return How many elements are in the queue
|
||||
*/
|
||||
size_t size() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
if (m_done)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
auto res = Container::size();
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes an element from the end of the queue.
|
||||
*
|
||||
* @return the removed element
|
||||
*/
|
||||
T pop()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock_cond(m_mutex);
|
||||
if (m_done)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
while (true)
|
||||
{
|
||||
m_cond.wait(lock_cond,
|
||||
[this] { return !Container::empty() || m_done; });
|
||||
if (m_done)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
T value =
|
||||
phx::front<Container>(*this); // for std::priority_queue
|
||||
Container::pop();
|
||||
if (validator(value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool try_pop(T& _value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
if (m_done)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (Container::empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_value = std::move(phx::front<Container>(*this));
|
||||
Container::pop();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief pushes an element to the front of the queue
|
||||
*
|
||||
* @param value The element to be pushed to the queue
|
||||
*/
|
||||
void push(const T& value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
if (m_done)
|
||||
{
|
||||
return;
|
||||
}
|
||||
bool unlock = false;
|
||||
if (Container::empty())
|
||||
{
|
||||
unlock = true;
|
||||
}
|
||||
Container::push(value);
|
||||
if (unlock)
|
||||
{
|
||||
m_cond.notify_one();
|
||||
}
|
||||
}
|
||||
void push(T&& value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
if (m_done)
|
||||
{
|
||||
return;
|
||||
}
|
||||
bool unlock = false;
|
||||
if (Container::empty())
|
||||
{
|
||||
unlock = true;
|
||||
}
|
||||
Container::push(std::move(value));
|
||||
if (unlock)
|
||||
{
|
||||
m_cond.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
void emplace(Args&&... args)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
if (m_done)
|
||||
{
|
||||
return;
|
||||
}
|
||||
bool unlock = false;
|
||||
if (Container::empty())
|
||||
{
|
||||
unlock = true;
|
||||
}
|
||||
Container::emplace(std::forward(args)...);
|
||||
if (unlock)
|
||||
{
|
||||
m_cond.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
public: // STL implementation of constructors
|
||||
explicit BlockingQueue(const Container& cont) : Container(cont) {}
|
||||
|
||||
explicit BlockingQueue(Container&& cont = Container())
|
||||
: Container(std::move(cont))
|
||||
{
|
||||
}
|
||||
|
||||
BlockingQueue(const BlockingQueue& other) : Container(other) {}
|
||||
|
||||
BlockingQueue(BlockingQueue&& other) : Container(std::move(other)) {}
|
||||
|
||||
template <class Alloc>
|
||||
explicit BlockingQueue(const Alloc& alloc) : Container(alloc)
|
||||
{
|
||||
}
|
||||
|
||||
template <class Alloc>
|
||||
BlockingQueue(const Container& cont, const Alloc& alloc)
|
||||
: Container(cont, alloc)
|
||||
{
|
||||
}
|
||||
|
||||
template <class Alloc>
|
||||
BlockingQueue(Container&& cont, const Alloc& alloc)
|
||||
: Container(std::move(cont), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
template <class Alloc>
|
||||
BlockingQueue(const BlockingQueue& other, const Alloc& alloc)
|
||||
: Container(other, alloc)
|
||||
{
|
||||
}
|
||||
|
||||
template <class Alloc>
|
||||
BlockingQueue(BlockingQueue&& other, const Alloc& alloc)
|
||||
: Container(other, alloc)
|
||||
{
|
||||
}
|
||||
|
||||
BlockingQueue& operator=(const BlockingQueue& _queue)
|
||||
{
|
||||
// std::lock_guard<std::mutex> lock(m_mutex);
|
||||
return *this;
|
||||
}
|
||||
|
||||
BlockingQueue& operator=(BlockingQueue&& _queue)
|
||||
{
|
||||
// std::lock_guard<std::mutex> lock(m_mutex);
|
||||
// std::lock_guard<std::mutex> lock2(_queue.m_mutex);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void lock() const { m_mutex.lock(); }
|
||||
|
||||
void unlock() const { m_mutex.unlock(); }
|
||||
|
||||
protected:
|
||||
mutable std::mutex m_mutex;
|
||||
|
||||
private:
|
||||
std::condition_variable m_cond;
|
||||
std::atomic<bool> m_done = false;
|
||||
Validator validator;
|
||||
};
|
||||
template <typename T, typename Validator, class Compare,
|
||||
typename Container = std::vector<T>>
|
||||
using PriorityBlockingQueue =
|
||||
BlockingQueue<T, Validator, std::priority_queue<T, Container, Compare>>;
|
||||
} // namespace phx
|
|
@ -0,0 +1,71 @@
|
|||
// Copyright 2019-20 Genten Studios
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace
|
||||
{
|
||||
namespace internal
|
||||
{
|
||||
template <class Container>
|
||||
struct front
|
||||
{
|
||||
static auto process(Container& t) { return t.front(); }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct front<std::string>
|
||||
{
|
||||
static auto process(std::string& t) { return t.front(); }
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct front<std::queue<T>>
|
||||
{
|
||||
static const T& process(std::queue<T>& t) { return t.front(); }
|
||||
};
|
||||
|
||||
template <class T, class... Args>
|
||||
struct front<std::priority_queue<T, Args...>>
|
||||
{
|
||||
static auto process(std::priority_queue<T, Args...>& t)
|
||||
{
|
||||
return t.top();
|
||||
}
|
||||
};
|
||||
} // namespace internal
|
||||
} // namespace
|
||||
|
||||
namespace phx
|
||||
{
|
||||
template <class Container>
|
||||
auto front(Container& t)
|
||||
{
|
||||
return internal::front<Container>::process(std::forward<Container&>(t));
|
||||
}
|
||||
} // namespace phx
|
|
@ -79,14 +79,14 @@ namespace phx::voxels
|
|||
public:
|
||||
Chunk() = delete;
|
||||
|
||||
explicit Chunk(math::vec3 chunkPos);
|
||||
explicit Chunk(const math::vec3& chunkPos);
|
||||
~Chunk() = default;
|
||||
Chunk(const Chunk& other) = default;
|
||||
Chunk& operator=(const Chunk& other) = default;
|
||||
Chunk(Chunk&& other) noexcept = default;
|
||||
Chunk& operator=(Chunk&& other) noexcept = default;
|
||||
|
||||
Chunk(math::vec3 chunkPos, const std::string& save);
|
||||
Chunk(const math::vec3& chunkPos, const std::string& save);
|
||||
|
||||
std::string save();
|
||||
|
||||
|
|
|
@ -37,11 +37,11 @@ namespace phx::voxels
|
|||
class Map
|
||||
{
|
||||
public:
|
||||
Map(std::string save, std::string name);
|
||||
Map(const std::string& save, const std::string& name);
|
||||
|
||||
Chunk getChunk(math::vec3 pos);
|
||||
Chunk getChunk(const math::vec3& pos);
|
||||
void setBlockAt(math::vec3 pos, BlockType* block);
|
||||
void save(math::vec3 pos);
|
||||
void save(const math::vec3& pos);
|
||||
|
||||
private:
|
||||
std::map<math::vec3, Chunk, math::Vector3Key> m_chunks;
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
// Copyright 2020 Genten Studios
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <Common/Actor.hpp>
|
||||
|
||||
#include <Common/Movement.hpp>
|
||||
#include <Common/Position.hpp>
|
||||
|
||||
using namespace phx;
|
||||
|
||||
entt::entity ActorSystem::registerActor(entt::registry* registry)
|
||||
{
|
||||
auto entity = registry->create();
|
||||
registry->emplace<Position>(entity, math::vec3 {0, 0, 0},
|
||||
math::vec3 {0, 0, 0});
|
||||
registry->emplace<Movement>(entity, DEFAULT_MOVE_SPEED);
|
||||
return entity;
|
||||
}
|
||||
void ActorSystem::tick(entt::registry* registry, entt::entity entity,
|
||||
const float dt, InputState input)
|
||||
{
|
||||
auto& pos = registry->get<Position>(entity);
|
||||
|
||||
/// conversion from 1/1000 of degres to rad
|
||||
pos.rotation.x = static_cast<float>(input.rotation.x) / 360000.0;
|
||||
pos.rotation.y = static_cast<float>(input.rotation.y) / 360000.0;
|
||||
const auto moveSpeed =
|
||||
static_cast<float>(registry->get<Movement>(entity).moveSpeed);
|
||||
|
||||
math::vec3 direction = pos.getDirection();
|
||||
const math::vec3 right = {std::sin(direction.x - math::PIDIV2), 0.f,
|
||||
std::cos(direction.x - math::PIDIV2)};
|
||||
const math::vec3 forward = {std::sin(direction.x), 0.f,
|
||||
std::cos(direction.x)};
|
||||
|
||||
if (input.forward)
|
||||
{
|
||||
pos.position += forward * dt * moveSpeed;
|
||||
}
|
||||
else if (input.backward)
|
||||
{
|
||||
pos.position -= forward * dt * moveSpeed;
|
||||
}
|
||||
|
||||
if (input.left)
|
||||
{
|
||||
pos.position -= right * dt * moveSpeed;
|
||||
}
|
||||
else if (input.right)
|
||||
{
|
||||
pos.position += right * dt * moveSpeed;
|
||||
}
|
||||
|
||||
if (input.up)
|
||||
{
|
||||
pos.position.y += dt * moveSpeed;
|
||||
}
|
||||
else if (input.down)
|
||||
{
|
||||
pos.position.y -= dt * moveSpeed;
|
||||
}
|
||||
}
|
|
@ -1,16 +1,20 @@
|
|||
add_subdirectory(Math)
|
||||
add_subdirectory(Voxels)
|
||||
add_subdirectory(CMS)
|
||||
add_subdirectory(Network)
|
||||
|
||||
set(currentDir ${CMAKE_CURRENT_LIST_DIR})
|
||||
set(Sources
|
||||
${mathSources}
|
||||
${voxelSources}
|
||||
${cmsSources}
|
||||
${networkSources}
|
||||
|
||||
${currentDir}/Actor.cpp
|
||||
${currentDir}/Settings.cpp
|
||||
${currentDir}/Logger.cpp
|
||||
${currentDir}/Commander.cpp
|
||||
${currentDir}/Input.cpp
|
||||
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright 2019-20 Genten Studios
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <Common/Input.hpp>
|
||||
|
||||
phx::Serializer& phx::InputState::operator&(phx::Serializer& this_)
|
||||
{
|
||||
return this_ & forward & backward & left & right & up & down & rotation.x & rotation.y & sequence;
|
||||
}
|
|
@ -183,8 +183,9 @@ Logger::Logger(const LoggerConfig& config)
|
|||
m_file.open(config.logFile);
|
||||
if (!m_file.is_open())
|
||||
{
|
||||
printf("Uh Oh! We couldn't open the log file, guess we won't have any "
|
||||
"file logging for today. :(\n");
|
||||
Log message = {LogVerbosity::INFO, "", 0, "LOGGING"};
|
||||
message << "A log file was not specified, logging only to console.";
|
||||
log(message);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
// Copyright 2019-20 Genten Studios
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <Common/Logger.hpp>
|
||||
#include <Common/Network/Address.hpp>
|
||||
|
||||
using namespace phx::net;
|
||||
|
||||
Address::Address(enet_uint16 port) : Address(ENET_HOST_ANY, port) {}
|
||||
|
||||
Address::Address(enet_uint32 host, enet_uint16 port)
|
||||
{
|
||||
m_address.host = host;
|
||||
m_address.port = port;
|
||||
}
|
||||
|
||||
Address::Address(const std::string& host, enet_uint16 port)
|
||||
{
|
||||
setHost(host);
|
||||
m_address.port = port;
|
||||
}
|
||||
|
||||
Address::Address(const ENetAddress& address) { m_address = address; }
|
||||
Address& Address::operator=(const ENetAddress& address)
|
||||
{
|
||||
m_address = address;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Address::Address(ENetAddress&& address) { m_address = address; }
|
||||
Address& Address::operator=(ENetAddress&& address)
|
||||
{
|
||||
m_address = address;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Address::setHost(const std::string& host)
|
||||
{
|
||||
if (enet_address_set_host(&m_address, host.c_str()))
|
||||
{
|
||||
LOG_FATAL("NETCODE") << "Could not resolve hostname.";
|
||||
}
|
||||
}
|
||||
|
||||
void Address::setHost(enet_uint32 host) { m_address.host = host; }
|
||||
|
||||
void Address::setPort(enet_uint16 port) { m_address.port = port; }
|
||||
|
||||
std::string Address::getHostname() const
|
||||
{
|
||||
// hostnames can be max of 255 bytes.
|
||||
constexpr int MaxHostNameBytes = 255;
|
||||
|
||||
// + 1 for null terminator.
|
||||
char hostname[MaxHostNameBytes + 1];
|
||||
if (enet_address_get_host(&m_address, hostname, MaxHostNameBytes))
|
||||
{
|
||||
LOG_FATAL("NETCODE") << "Could not resolve hostname.";
|
||||
}
|
||||
|
||||
return hostname;
|
||||
}
|
||||
|
||||
std::string Address::getIP() const
|
||||
{
|
||||
// enet doesn't support ipv6 afaik.
|
||||
constexpr int IPv4MaxBytes = 15;
|
||||
|
||||
std::string ip;
|
||||
|
||||
// + 1 for null terminator.
|
||||
char ipRaw[IPv4MaxBytes + 1];
|
||||
if (enet_address_get_host_ip(&m_address, ipRaw, IPv4MaxBytes))
|
||||
{
|
||||
LOG_FATAL("NETCODE") << "Failed to get IP.";
|
||||
ip = "Unknown IP";
|
||||
}
|
||||
else
|
||||
{
|
||||
ip = ipRaw;
|
||||
}
|
||||
|
||||
return ip;
|
||||
}
|
||||
|
||||
enet_uint16 Address::getPort() const { return m_address.port; }
|
|
@ -0,0 +1,9 @@
|
|||
set(currentDir ${CMAKE_CURRENT_LIST_DIR})
|
||||
set(networkSources
|
||||
${currentDir}/Address.cpp
|
||||
${currentDir}/Packet.cpp
|
||||
${currentDir}/Peer.cpp
|
||||
${currentDir}/Host.cpp
|
||||
|
||||
PARENT_SCOPE
|
||||
)
|
|
@ -0,0 +1,247 @@
|
|||
// Copyright 2019-20 Genten Studios
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <Common/Logger.hpp>
|
||||
#include <Common/Network/Host.hpp>
|
||||
|
||||
#include <utility>
|
||||
|
||||
using namespace phx::net;
|
||||
|
||||
std::atomic<std::size_t> Host::m_activeInstances = 0;
|
||||
|
||||
Host::Host(std::size_t peers, const ENetAddress* address)
|
||||
: Host(address ? Address {*address} : Address {}, peers)
|
||||
{
|
||||
}
|
||||
|
||||
Host::Host(const Address& address, std::size_t peers, std::size_t channels)
|
||||
{
|
||||
if (m_activeInstances == 0)
|
||||
{
|
||||
if (enet_initialize())
|
||||
{
|
||||
LOG_FATAL("NETCODE") << "Failed to initialize ENet networking.";
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
++m_activeInstances;
|
||||
|
||||
m_host = enet_host_create(address, peers, channels, 0, 0);
|
||||
}
|
||||
|
||||
Host::~Host()
|
||||
{
|
||||
--m_activeInstances;
|
||||
|
||||
enet_host_destroy(m_host);
|
||||
|
||||
if (m_activeInstances == 0)
|
||||
{
|
||||
enet_deinitialize();
|
||||
}
|
||||
}
|
||||
|
||||
Host::OptionalPeer Host::connect(const Address& address)
|
||||
{
|
||||
return connect(address, getChannelLimit());
|
||||
}
|
||||
|
||||
Host::OptionalPeer Host::connect(const Address& address, enet_uint8 channels,
|
||||
enet_uint32 data)
|
||||
{
|
||||
ENetPeer* peer = enet_host_connect(m_host, address, channels, data);
|
||||
|
||||
if (!peer)
|
||||
{
|
||||
LOG_FATAL("Failed to connect to peer.");
|
||||
return {};
|
||||
}
|
||||
|
||||
return createPeer(*peer);
|
||||
}
|
||||
|
||||
Bandwidth Host::getBandwidthLimit() const
|
||||
{
|
||||
return {m_host->incomingBandwidth, m_host->outgoingBandwidth};
|
||||
}
|
||||
|
||||
void Host::setBandwidthLimit(const Bandwidth& bandwidth)
|
||||
{
|
||||
enet_host_bandwidth_limit(m_host, bandwidth.incoming, bandwidth.outgoing);
|
||||
}
|
||||
|
||||
std::size_t Host::getChannelLimit() const { return m_host->channelLimit; }
|
||||
|
||||
void Host::setChannelLimit(std::size_t limit)
|
||||
{
|
||||
enet_host_channel_limit(m_host, limit);
|
||||
}
|
||||
|
||||
void Host::broadcast(Packet& packet, enet_uint8 channel)
|
||||
{
|
||||
packet.prepareForSend();
|
||||
enet_host_broadcast(m_host, channel, packet);
|
||||
}
|
||||
|
||||
void Host::broadcast(Packet&& packet, enet_uint8 channel)
|
||||
{
|
||||
packet.prepareForSend();
|
||||
enet_host_broadcast(m_host, channel, packet);
|
||||
}
|
||||
|
||||
void Host::onReceive(ReceiveCallback callback)
|
||||
{
|
||||
m_receiveCallback = std::move(callback);
|
||||
}
|
||||
|
||||
void Host::onConnect(ConnectCallback callback)
|
||||
{
|
||||
m_connectCallback = std::move(callback);
|
||||
}
|
||||
|
||||
void Host::onDisconnect(DisconnectCallback callback)
|
||||
{
|
||||
m_disconnectCallback = std::move(callback);
|
||||
}
|
||||
|
||||
void Host::poll(int limit) { poll(0_ms, limit); }
|
||||
|
||||
void Host::poll(phx::time::ms timeout, int limit)
|
||||
{
|
||||
ENetEvent event;
|
||||
|
||||
do
|
||||
{
|
||||
if (enet_host_service(m_host, &event, timeout.count()))
|
||||
{
|
||||
handleEvent(event);
|
||||
}
|
||||
} while (--limit);
|
||||
}
|
||||
|
||||
void Host::flush() { enet_host_flush(m_host); }
|
||||
|
||||
std::size_t Host::getPeerCount() const { return m_host->connectedPeers; }
|
||||
|
||||
std::size_t Host::getPeerLimit() const { return m_host->peerCount; }
|
||||
|
||||
const Address& Host::getAddress() const { return m_address; }
|
||||
|
||||
Peer* Host::getPeer(std::size_t id)
|
||||
{
|
||||
if (m_peers.find(id) != m_peers.end())
|
||||
{
|
||||
return &m_peers[id];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
enet_uint32 Host::getTotalReceievedData() const
|
||||
{
|
||||
return m_host->totalReceivedData;
|
||||
}
|
||||
|
||||
enet_uint32 Host::getTotalSentData() const { return m_host->totalSentData; }
|
||||
|
||||
void Host::removePeer(const Peer& peer)
|
||||
{
|
||||
removePeer(*static_cast<ENetPeer*>(peer));
|
||||
}
|
||||
|
||||
void Host::handleEvent(ENetEvent& event)
|
||||
{
|
||||
ENetPeer* peer = event.peer;
|
||||
|
||||
switch (event.type)
|
||||
{
|
||||
case ENET_EVENT_TYPE_CONNECT:
|
||||
if (m_connectCallback)
|
||||
{
|
||||
m_connectCallback(createPeer(*peer), event.data);
|
||||
}
|
||||
break;
|
||||
|
||||
case ENET_EVENT_TYPE_RECEIVE:
|
||||
if (m_receiveCallback)
|
||||
{
|
||||
m_receiveCallback(getPeer(*peer), Packet(*event.packet, true),
|
||||
event.channelID);
|
||||
}
|
||||
|
||||
enet_packet_destroy(event.packet);
|
||||
break;
|
||||
|
||||
case ENET_EVENT_TYPE_DISCONNECT:
|
||||
if (m_disconnectCallback)
|
||||
{
|
||||
m_disconnectCallback(std::size_t(peer->data), event.data);
|
||||
}
|
||||
removePeer(*peer);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Peer& Host::getPeer(ENetPeer& peer)
|
||||
{
|
||||
if (m_peers.find(std::size_t(peer.data)) != m_peers.end())
|
||||
{
|
||||
return m_peers.at(std::size_t(peer.data));
|
||||
}
|
||||
std::cout << "peer not found";
|
||||
}
|
||||
|
||||
Peer& Host::createPeer(ENetPeer& peer)
|
||||
{
|
||||
++m_peerID;
|
||||
peer.data = reinterpret_cast<void*>(m_peerID);
|
||||
m_peers.insert({m_peerID, {*this, peer}});
|
||||
return m_peers.at(m_peerID);
|
||||
}
|
||||
|
||||
void Host::removePeer(ENetPeer& peer)
|
||||
{
|
||||
auto id = std::size_t(peer.data);
|
||||
m_peers.erase(id);
|
||||
|
||||
peer.data = nullptr;
|
||||
}
|
||||
|
||||
void Host::disconnectPeer(std::size_t id)
|
||||
{
|
||||
if (m_disconnectCallback)
|
||||
{
|
||||
m_disconnectCallback(id, 0);
|
||||
}
|
||||
|
||||
removePeer(m_peers.at(id));
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
// Copyright 2019-20 Genten Studios
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <Common/Logger.hpp>
|
||||
#include <Common/Network/Packet.hpp>
|
||||
|
||||
using namespace phx::net;
|
||||
|
||||
Packet::Packet(const Data& data, PacketFlags flags)
|
||||
{
|
||||
// unreliable is fake just cos so removing it.
|
||||
create(data, flags & ~PacketFlags::UNRELIABLE);
|
||||
}
|
||||
|
||||
Packet::Packet(std::size_t size, PacketFlags flags)
|
||||
: Packet(*enet_packet_create(
|
||||
nullptr, size,
|
||||
static_cast<enet_uint32>(flags & ~PacketFlags::UNRELIABLE)), false)
|
||||
{
|
||||
}
|
||||
|
||||
Packet::Packet(ENetPacket& packet, bool sent) : m_packet(&packet), m_sent(sent)
|
||||
{
|
||||
}
|
||||
|
||||
Packet::~Packet()
|
||||
{
|
||||
if (!m_sent)
|
||||
{
|
||||
enet_packet_destroy(m_packet);
|
||||
}
|
||||
}
|
||||
|
||||
void Packet::setData(const Data& data)
|
||||
{
|
||||
if (m_sent)
|
||||
{
|
||||
// packet has already been sent.
|
||||
LOG_DEBUG("NETCODE") << "Packet has already been sent.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.size() != m_packet->dataLength)
|
||||
{
|
||||
enet_packet_resize(m_packet, data.size());
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < data.size(); ++i)
|
||||
{
|
||||
enet_uint8* ptr = m_packet->data + i;
|
||||
*ptr = static_cast<enet_uint8>(data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
Packet& Packet::operator=(const Data& data)
|
||||
{
|
||||
if (m_sent)
|
||||
{
|
||||
// packet has already been sent.
|
||||
LOG_DEBUG("NETCODE") << "Packet has already been sent.";
|
||||
return *this;
|
||||
}
|
||||
|
||||
setData(data);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Packet::Data Packet::getData() const
|
||||
{
|
||||
return {
|
||||
reinterpret_cast<std::byte*>(m_packet->data),
|
||||
reinterpret_cast<std::byte*>(m_packet->data + m_packet->dataLength)};
|
||||
}
|
||||
|
||||
void Packet::resize(std::size_t size)
|
||||
{
|
||||
if (m_sent)
|
||||
{
|
||||
// packet has already been sent.
|
||||
LOG_DEBUG("NETCODE") << "Packet has already been sent.";
|
||||
return;
|
||||
}
|
||||
|
||||
enet_packet_resize(m_packet, size);
|
||||
}
|
||||
|
||||
std::size_t Packet::getSize() const { return m_packet->dataLength; }
|
||||
|
||||
void Packet::prepareForSend() { m_sent = true; }
|
||||
|
||||
void Packet::create(const Data& data, PacketFlags flags)
|
||||
{
|
||||
m_packet = enet_packet_create(data.data(), data.size(),
|
||||
static_cast<enet_uint32>(flags));
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
// Copyright 2019-20 Genten Studios
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <Common/Logger.hpp>
|
||||
#include <Common/Network/Host.hpp>
|
||||
#include <Common/Network/Peer.hpp>
|
||||
|
||||
using namespace phx::net;
|
||||
|
||||
Peer::Peer(Host& host, ENetPeer& peer) : m_peer(&peer), m_host(&host) {}
|
||||
|
||||
Peer& Peer::operator=(ENetPeer& peer)
|
||||
{
|
||||
m_peer = &peer;
|
||||
m_address = peer.address;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Peer::disconnect(enet_uint32 data) { enet_peer_disconnect(m_peer, data); }
|
||||
|
||||
void Peer::disconnectImmediately(enet_uint32 data)
|
||||
{
|
||||
// doing this doesn't produce a disconnect event on the host, so we manually
|
||||
// trigger the disconnection callback.
|
||||
std::size_t id = getID();
|
||||
enet_peer_disconnect_now(m_peer, data);
|
||||
m_host->disconnectPeer(id);
|
||||
}
|
||||
|
||||
void Peer::disconnectOncePacketsAreSent(enet_uint32 data)
|
||||
{
|
||||
enet_peer_disconnect_later(m_peer, data);
|
||||
}
|
||||
|
||||
void Peer::drop()
|
||||
{
|
||||
// doing this doesn't produce a disconnect event on the host, so we manually
|
||||
// trigger the disconnection callback.
|
||||
std::size_t id = getID();
|
||||
enet_peer_reset(m_peer);
|
||||
m_host->disconnectPeer(id);
|
||||
}
|
||||
|
||||
void Peer::ping() const { enet_peer_ping(m_peer); }
|
||||
|
||||
phx::time::ms Peer::getPingInterval() const
|
||||
{
|
||||
return phx::time::ms {m_peer->pingInterval};
|
||||
}
|
||||
|
||||
void Peer::setPingInterval(phx::time::ms interval)
|
||||
{
|
||||
enet_peer_ping_interval(m_peer, interval.count());
|
||||
}
|
||||
|
||||
phx::time::ms Peer::getRoundTripTime() const
|
||||
{
|
||||
return phx::time::ms {m_peer->roundTripTime};
|
||||
}
|
||||
|
||||
enet_uint32 Peer::getPacketLoss() const { return m_peer->packetLoss; }
|
||||
|
||||
void Peer::receive(Callback callback) const
|
||||
{
|
||||
enet_uint8 channel;
|
||||
auto packet = enet_peer_receive(m_peer, &channel);
|
||||
callback(Packet {*packet, true}, channel);
|
||||
}
|
||||
|
||||
void Peer::send(Packet& packet, enet_uint8 channel)
|
||||
{
|
||||
packet.prepareForSend();
|
||||
enet_peer_send(m_peer, channel, packet);
|
||||
}
|
||||
|
||||
void Peer::send(Packet&& packet, enet_uint8 channel)
|
||||
{
|
||||
packet.prepareForSend();
|
||||
enet_peer_send(m_peer, channel, packet);
|
||||
}
|
||||
|
||||
Throttle Peer::getThrottle() const
|
||||
{
|
||||
return {phx::time::ms {m_peer->packetThrottleInterval},
|
||||
m_peer->packetThrottleAcceleration,
|
||||
m_peer->packetThrottleDeceleration};
|
||||
}
|
||||
|
||||
void Peer::setThrottle(const Throttle& throttle)
|
||||
{
|
||||
enet_peer_throttle_configure(m_peer, throttle.interval.count(),
|
||||
throttle.acceleration, throttle.deceleration);
|
||||
}
|
||||
|
||||
Timeout Peer::getTimeout() const
|
||||
{
|
||||
return {
|
||||
phx::time::ms {m_peer->timeoutLimit},
|
||||
phx::time::ms {m_peer->timeoutMinimum},
|
||||
phx::time::ms {m_peer->timeoutMaximum},
|
||||
};
|
||||
}
|
||||
|
||||
void Peer::setTimeout(const Timeout& timeout)
|
||||
{
|
||||
enet_peer_timeout(m_peer, timeout.limit.count(), timeout.minimum.count(),
|
||||
timeout.maximum.count());
|
||||
}
|
||||
|
||||
const Address& Peer::getAddress() const { return m_address; }
|
||||
|
||||
PeerStatus Peer::getState() const
|
||||
{
|
||||
return static_cast<PeerStatus>(m_peer->state);
|
||||
}
|
|
@ -32,7 +32,6 @@
|
|||
#include <cstring>
|
||||
|
||||
using namespace phx::voxels;
|
||||
using namespace phx;
|
||||
|
||||
BlockRegistry::BlockRegistry()
|
||||
{
|
||||
|
@ -50,7 +49,7 @@ BlockRegistry::BlockRegistry()
|
|||
registerBlock(outOfBoundsBlock);
|
||||
}
|
||||
|
||||
void BlockRegistry::registerAPI(cms::ModManager* manager)
|
||||
void BlockRegistry::registerAPI(phx::cms::ModManager* manager)
|
||||
{
|
||||
manager->registerFunction(
|
||||
"voxel.block.register", [manager](sol::table luaBlock) {
|
||||
|
|
|
@ -31,14 +31,14 @@
|
|||
#include <iostream>
|
||||
|
||||
using namespace phx::voxels;
|
||||
using namespace phx;
|
||||
|
||||
Chunk::Chunk(math::vec3 chunkPos) : m_pos(chunkPos)
|
||||
Chunk::Chunk(const phx::math::vec3& chunkPos) : m_pos(chunkPos)
|
||||
{
|
||||
m_blocks.reserve(CHUNK_WIDTH * CHUNK_HEIGHT * CHUNK_DEPTH);
|
||||
}
|
||||
|
||||
Chunk::Chunk(math::vec3 chunkPos, const std::string& save) : m_pos(chunkPos)
|
||||
Chunk::Chunk(const phx::math::vec3& chunkPos, const std::string& save)
|
||||
: m_pos(chunkPos)
|
||||
{
|
||||
std::string_view search = save;
|
||||
size_t pos;
|
||||
|
@ -76,10 +76,10 @@ void Chunk::autoTestFill()
|
|||
}
|
||||
}
|
||||
|
||||
math::vec3 Chunk::getChunkPos() const { return m_pos; }
|
||||
phx::math::vec3 Chunk::getChunkPos() const { return m_pos; }
|
||||
std::vector<BlockType*>& Chunk::getBlocks() { return m_blocks; }
|
||||
|
||||
BlockType* Chunk::getBlockAt(math::vec3 position) const
|
||||
BlockType* Chunk::getBlockAt(phx::math::vec3 position) const
|
||||
{
|
||||
if (position.x < CHUNK_WIDTH && position.y < CHUNK_HEIGHT &&
|
||||
position.z < CHUNK_DEPTH)
|
||||
|
@ -91,7 +91,7 @@ BlockType* Chunk::getBlockAt(math::vec3 position) const
|
|||
1); // 1 is always out of bounds
|
||||
}
|
||||
|
||||
void Chunk::setBlockAt(math::vec3 position, BlockType* newBlock)
|
||||
void Chunk::setBlockAt(phx::math::vec3 position, BlockType* newBlock)
|
||||
{
|
||||
if (position.x < CHUNK_WIDTH && position.y < CHUNK_HEIGHT &&
|
||||
position.z < CHUNK_DEPTH)
|
||||
|
@ -99,4 +99,3 @@ void Chunk::setBlockAt(math::vec3 position, BlockType* newBlock)
|
|||
m_blocks[getVectorIndex(position)] = newBlock;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,14 +32,13 @@
|
|||
#include <utility>
|
||||
|
||||
using namespace phx::voxels;
|
||||
using namespace phx;
|
||||
|
||||
Map::Map(std::string save, std::string name)
|
||||
: m_save(std::move(save)), m_mapName(std::move(name))
|
||||
Map::Map(const std::string& save, const std::string& name)
|
||||
: m_save(save), m_mapName(name)
|
||||
{
|
||||
}
|
||||
|
||||
Chunk Map::getChunk(math::vec3 pos)
|
||||
Chunk Map::getChunk(const phx::math::vec3& pos)
|
||||
{
|
||||
if (m_chunks.find(pos) != m_chunks.end())
|
||||
{
|
||||
|
@ -117,7 +116,7 @@ void Map::setBlockAt(phx::math::vec3 position, BlockType* block)
|
|||
save(chunkPosition);
|
||||
}
|
||||
|
||||
void Map::save(phx::math::vec3 pos)
|
||||
void Map::save(const phx::math::vec3& pos)
|
||||
{
|
||||
std::ofstream saveFile;
|
||||
std::string position = "." + std::to_string(int(pos.x)) + "_" +
|
||||
|
|
|
@ -9,7 +9,8 @@ function hello (args)
|
|||
print("with you, the force is not")
|
||||
end
|
||||
end
|
||||
core.command.register("Hello", "Master the arts of the Jedi you must", hello)
|
||||
|
||||
--core.command.register("Hello", "Master the arts of the Jedi you must", hello)
|
||||
|
||||
local dirtAudioRef = audio.loadMP3("core.dirt_place", "Modules/mod1/Assets/Audio/dirt_place.mp3");
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
set(currentDir ${CMAKE_CURRENT_LIST_DIR})
|
||||
set(Headers
|
||||
${currentDir}/Server.hpp
|
||||
${currentDir}/Server.hpp
|
||||
${currentDir}/Iris.hpp
|
||||
${currentDir}/Game.hpp
|
||||
${currentDir}/Commander.hpp
|
||||
|
||||
PARENT_SCOPE
|
||||
)
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
// Copyright 2019-20 Genten Studios
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
/**
|
||||
* @file Commander.hpp
|
||||
* @brief Header file to implement a command execution system designed to
|
||||
* interface with a terminal.
|
||||
*
|
||||
* @copyright Copyright (c) 2019-2020
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Server/Iris.hpp>
|
||||
|
||||
#include <Common/CMS/ModManager.hpp>
|
||||
|
||||
#include <entt/entity/registry.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace phx::server
|
||||
{
|
||||
/**
|
||||
* @brief A function that is called when a command is executed.
|
||||
*
|
||||
* The function should take a vector of strings. Each string is an argument
|
||||
* similar to how programs are called from the terminal.
|
||||
*
|
||||
*/
|
||||
using CommandFunction = std::function<void(std::vector<std::string> args)>;
|
||||
|
||||
struct Command
|
||||
{
|
||||
std::string command;
|
||||
std::string help;
|
||||
CommandFunction callback;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The command book stores commands and information on them to be
|
||||
* used by a commander.
|
||||
*/
|
||||
class Commander
|
||||
{
|
||||
public:
|
||||
explicit Commander(net::Iris* iris);
|
||||
|
||||
void registerAPI(cms::ModManager* manager);
|
||||
|
||||
/**
|
||||
* @brief Registers a command in the command registry.
|
||||
*
|
||||
* If a command already exists in the registry, this function will
|
||||
* over write that command with the new data.
|
||||
*
|
||||
* @param command The keyword for calling the command.
|
||||
* @param help A help string that can be displayed to the user.
|
||||
* @param f The function that is called when the command is executed.
|
||||
*/
|
||||
void add(const std::string& command, const std::string& help,
|
||||
const CommandFunction& f);
|
||||
|
||||
/**
|
||||
* @brief Calls a command.
|
||||
*
|
||||
* @param command The keyword for calling the command.
|
||||
* @param args The arguments to be passed to the command.
|
||||
* @param userRef The user who ran the command
|
||||
* @return Returns True if the function was called and False if the
|
||||
* function could not be found
|
||||
*/
|
||||
bool run(std::size_t userRef, const std::string& input);
|
||||
|
||||
/**
|
||||
* @brief Returns helpstring for command.
|
||||
*
|
||||
* @param args array of input, args[0] is the command helpstring is
|
||||
* returned for, all other array values are not used.
|
||||
* @param userRef The user who ran the command
|
||||
* @return Returns True if successful and False if it could not find
|
||||
* the inputted command.
|
||||
*/
|
||||
bool help(std::size_t userRef, const std::vector<std::string>& args);
|
||||
|
||||
/**
|
||||
* @brief Outputs a string listing available commands.
|
||||
*
|
||||
* @param userRef The user who ran the command
|
||||
*/
|
||||
void list(std::size_t userRef);
|
||||
|
||||
private:
|
||||
net::Iris* m_iris;
|
||||
std::unordered_map<std::string, Command> m_commands;
|
||||
};
|
||||
} // namespace phx
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright 2019-20 Genten Studios
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Server/Commander.hpp>
|
||||
#include <Server/Iris.hpp>
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
|
||||
namespace phx::server
|
||||
{
|
||||
class Game
|
||||
{
|
||||
public:
|
||||
/** @brief The server side game object, this handles all of the core
|
||||
* game logic.
|
||||
*
|
||||
* @param registry The shared EnTT registry
|
||||
* @param running Pointer to a boolean, the threaded function only runs
|
||||
* if this is true
|
||||
* @param iris Pointer to the nextworking system
|
||||
*/
|
||||
Game(entt::registry* registry, bool* running, net::Iris* iris);
|
||||
|
||||
/** @brief Loads all API's that the game utilizes into a CMS ModManager
|
||||
*
|
||||
* @param manager The mod manager to load the API into
|
||||
*/
|
||||
void registerAPI(cms::ModManager* manager);
|
||||
|
||||
/**
|
||||
* @brief Runs the main game loop as long as running is true
|
||||
*/
|
||||
void run();
|
||||
|
||||
/// @brief Just a temporary static storage for the DT
|
||||
/// @TODO Move this to a config file
|
||||
static constexpr float dt = 1.f / 20.f;
|
||||
|
||||
private:
|
||||
/// @brief The main loop runs while this is true
|
||||
bool* m_running;
|
||||
/// @breif An EnTT registry to store various data in
|
||||
entt::registry* m_registry;
|
||||
/// @brief The networking object to get data from
|
||||
net::Iris* m_iris;
|
||||
/// @brief A commander object to process commands
|
||||
Commander* m_commander;
|
||||
};
|
||||
} // namespace phx::server
|
|
@ -0,0 +1,161 @@
|
|||
// Copyright 2019-20 Genten Studios
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
// This is needed because Windows https://github.com/skypjack/entt/issues/96
|
||||
#ifndef NOMINMAX
|
||||
# define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <Common/Input.hpp>
|
||||
#include <Common/Network/Host.hpp>
|
||||
#include <Common/Util/BlockingQueue.hpp>
|
||||
|
||||
#include <enet/enet.h>
|
||||
#include <entt/entt.hpp>
|
||||
|
||||
namespace phx::server::net
|
||||
{
|
||||
struct StateBundle
|
||||
{
|
||||
bool ready;
|
||||
std::size_t users;
|
||||
std::size_t sequence;
|
||||
std::unordered_map<entt::entity, InputState> states;
|
||||
};
|
||||
|
||||
struct MessageBundle
|
||||
{
|
||||
size_t userID;
|
||||
std::string message;
|
||||
};
|
||||
|
||||
class Iris
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Creates a networking object to handle listening for packets
|
||||
*
|
||||
* @param registry The shared EnTT registry
|
||||
* @param running Pointer to a boolean, the threaded function only runs
|
||||
* if this is true
|
||||
*/
|
||||
Iris(entt::registry* registry);
|
||||
|
||||
/**
|
||||
* @brief Cleans up any internal only objects
|
||||
*/
|
||||
~Iris();
|
||||
|
||||
/**
|
||||
* @brief Loops listening to the netowork and populates queues for data
|
||||
* consumption
|
||||
*/
|
||||
void run();
|
||||
|
||||
void kill() { m_running = false; };
|
||||
|
||||
/**
|
||||
* @brief Actions taken when a user disconnects
|
||||
*
|
||||
* @param userRef The user who disconnected
|
||||
*/
|
||||
void disconnect(std::size_t peerID);
|
||||
|
||||
/**
|
||||
* @brief Actions taken when an event is received
|
||||
*
|
||||
* @param userRef The user who sent the event packet
|
||||
* @param data The data in the event packet
|
||||
* @param dataLength The length of the data in the event packet
|
||||
*/
|
||||
void parseEvent(std::size_t userID, phx::net::Packet& packet);
|
||||
|
||||
/**
|
||||
* @brief Actions taken when a state is received
|
||||
*
|
||||
* @param userRef The user who sent the state packet
|
||||
* @param data The data in the state packet
|
||||
* @param dataLength The length of the data in the state packet
|
||||
*/
|
||||
void parseState(std::size_t userID, phx::net::Packet& packet);
|
||||
|
||||
/**
|
||||
* @brief Actions taken when a message is received
|
||||
*
|
||||
* @param userRef The user who sent the message packet
|
||||
* @param data The data in the message packet
|
||||
* @param dataLength The length of the data in the message packet
|
||||
*/
|
||||
void parseMessage(std::size_t userID, phx::net::Packet& packet);
|
||||
|
||||
/**
|
||||
* @brief Sends an event packet to a client
|
||||
*
|
||||
* @param userRef The user to sent the event to
|
||||
* @param data The event packet data
|
||||
*/
|
||||
void sendEvent(std::size_t userID, enet_uint8* data);
|
||||
|
||||
/**
|
||||
* @brief Sends a state packet to a client
|
||||
*
|
||||
* @param userRef The user to sent the state to
|
||||
* @param data The state packet data
|
||||
*/
|
||||
void sendState(entt::registry* registry, std::size_t sequence);
|
||||
|
||||
/**
|
||||
* @brief Sends a message packet to a client
|
||||
*
|
||||
* @note Const ref's are not used here due to the nature of the
|
||||
* serializer
|
||||
*
|
||||
* @param userRef The user to sent the message to
|
||||
* @param data The message packet data
|
||||
*/
|
||||
void sendMessage(std::size_t userID, std::string message);
|
||||
|
||||
/**
|
||||
* @brief The Queue of bundled states received
|
||||
*/
|
||||
std::vector<StateBundle> currentBundles;
|
||||
BlockingQueue<StateBundle> stateQueue;
|
||||
/**
|
||||
* @brief The Queue of messages received
|
||||
*/
|
||||
BlockingQueue<MessageBundle> messageQueue;
|
||||
|
||||
private:
|
||||
bool m_running;
|
||||
phx::net::Host* m_server;
|
||||
entt::registry* m_registry;
|
||||
std::unordered_map<std::size_t, entt::entity> m_users;
|
||||
};
|
||||
} // namespace phx::server::net
|
|
@ -28,18 +28,53 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <Server/Game.hpp>
|
||||
#include <Server/Iris.hpp>
|
||||
|
||||
#include <Server/User.hpp>
|
||||
|
||||
#include <Common/CMS/ModManager.hpp>
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
#include <enet/enet.h>
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
|
||||
namespace phx::server
|
||||
{
|
||||
class Server
|
||||
{
|
||||
public:
|
||||
Server() = default;
|
||||
~Server() = default;
|
||||
/**
|
||||
* @brief Core object for the server
|
||||
*
|
||||
* @param save The save we are loading
|
||||
*/
|
||||
Server(std::string save);
|
||||
~Server();
|
||||
|
||||
/// @brief Main loop for the server
|
||||
void run();
|
||||
|
||||
private:
|
||||
bool m_running;
|
||||
/// @brief central boolean to control if the game is running or not
|
||||
bool m_running = true;
|
||||
|
||||
/// @breif An EnTT registry to store various data in
|
||||
entt::registry m_registry;
|
||||
|
||||
/// @brief The networking object, this listens for incoming data
|
||||
net::Iris* m_iris;
|
||||
/** @brief @brief The server side game object, this handles all of the
|
||||
* core game logic.
|
||||
*/
|
||||
Game* m_game;
|
||||
|
||||
/// @brief The mod manager providing LUA API functionality
|
||||
cms::ModManager* m_modManager;
|
||||
|
||||
/// The name of the save we are running
|
||||
std::string m_save;
|
||||
};
|
||||
} // namespace phx::server
|
||||
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright 2019-20 Genten Studios
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <enet/enet.h>
|
||||
#include <entt/entt.hpp>
|
||||
#include <string>
|
||||
|
||||
namespace phx::server
|
||||
{
|
||||
struct User
|
||||
{
|
||||
std::string userName;
|
||||
ENetPeer* peer;
|
||||
};
|
||||
|
||||
struct Player
|
||||
{
|
||||
entt::entity actor;
|
||||
};
|
||||
} // namespace phx::server
|
|
@ -1,8 +1,11 @@
|
|||
set(currentDir ${CMAKE_CURRENT_LIST_DIR})
|
||||
set(Sources
|
||||
${currentDir}/Server.cpp
|
||||
${currentDir}/Server.cpp
|
||||
${currentDir}/Iris.cpp
|
||||
${currentDir}/Game.cpp
|
||||
${currentDir}/Commander.cpp
|
||||
|
||||
${currentDir}/Main.cpp
|
||||
${currentDir}/Main.cpp
|
||||
|
||||
PARENT_SCOPE
|
||||
)
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
|
|
@ -0,0 +1,212 @@
|
|||
// Copyright 2019-20 Genten Studios
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
/**
|
||||
* @file Commander.cpp
|
||||
* @brief Source file to implement a command execution system designed to
|
||||
* interface with a terminal.
|
||||
*
|
||||
* @copyright Copyright (c) 2019-2020
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Common/Logger.hpp>
|
||||
#include <Server/Commander.hpp>
|
||||
|
||||
using namespace phx::server;
|
||||
|
||||
Commander::Commander(net::Iris* iris) : m_iris(iris) {}
|
||||
|
||||
void Commander::registerAPI(phx::cms::ModManager* manager)
|
||||
{
|
||||
/**
|
||||
* @addtogroup luaapi
|
||||
*
|
||||
* @subsubsection corecmdreg core.command.register
|
||||
* @brief Registers a new command
|
||||
*
|
||||
* In the terminal typing "/" followed by a command will execute
|
||||
the *command
|
||||
*
|
||||
* @param command The command to register
|
||||
* @param help A helpstring that is printed to terminal when
|
||||
typing
|
||||
*"/help <command>"
|
||||
* @param f The callback function that is called by the commander
|
||||
* The callback function must take a table as an argument
|
||||
* Any arguments included when the command is executed will be
|
||||
passed in *this table
|
||||
*
|
||||
* @b Example:
|
||||
* @code {.lua}
|
||||
* function hello (args)
|
||||
* if args[1] == "there" then
|
||||
* print("General Kenobi")
|
||||
* elseif args[1] == "world" then
|
||||
* print("World says hi")
|
||||
* else
|
||||
* print("with you, the force is not")
|
||||
* end
|
||||
* end
|
||||
* core.command.register("Hello", "Master the arts of the Jedi you
|
||||
*must", hello)
|
||||
* @endcode
|
||||
*/
|
||||
manager->registerFunction(
|
||||
"core.command.register",
|
||||
[this](std::string command, std::string help, sol::function f) {
|
||||
this->add(command, help, f);
|
||||
});
|
||||
}
|
||||
|
||||
void Commander::add(const std::string& command, const std::string& help,
|
||||
const CommandFunction& f)
|
||||
{
|
||||
if (m_commands.find(command) != m_commands.end())
|
||||
{
|
||||
LOG_INFO("COMMANDER") << "Command overwritten: " << command;
|
||||
}
|
||||
m_commands[command] = {command, help, f};
|
||||
}
|
||||
|
||||
bool Commander::run(std::size_t userRef, const std::string& input)
|
||||
{
|
||||
// Break the command into args
|
||||
std::string_view search = input;
|
||||
std::string command;
|
||||
std::string arg; // Just used to copy the args out of the search string.
|
||||
std::vector<std::string> args;
|
||||
size_t searchLoc;
|
||||
size_t spaceLoc;
|
||||
|
||||
if (!search.empty() && search[0] == '/')
|
||||
{
|
||||
// @TODO Can't enter \t or \n rn, might be a good idea to sanitize later
|
||||
// Search `first_of` initially, it's faster when there's no spaces.
|
||||
spaceLoc = search.find_first_of(' ');
|
||||
|
||||
// if we don't have arguments don't try and populate the args array.
|
||||
if (spaceLoc != std::string_view::npos)
|
||||
{
|
||||
command = search.substr(1, spaceLoc - 1);
|
||||
|
||||
search.remove_prefix(spaceLoc);
|
||||
|
||||
while ((searchLoc = search.find_first_not_of(' ')) !=
|
||||
std::string_view::npos)
|
||||
{
|
||||
search.remove_prefix(searchLoc); // strip the leading whitespace
|
||||
spaceLoc = search.find_first_of(' ');
|
||||
|
||||
if (spaceLoc != std::string_view::npos)
|
||||
{
|
||||
arg =
|
||||
search.substr(0, spaceLoc); // gen new string from view
|
||||
args.push_back(arg);
|
||||
}
|
||||
|
||||
// Don't forget to double check if spaceLoc is npos before we
|
||||
// remove_prefix, otherwise we might end up out of bounds...
|
||||
else
|
||||
{
|
||||
arg = search.substr(0, search.length());
|
||||
args.push_back(arg);
|
||||
break;
|
||||
}
|
||||
|
||||
search.remove_prefix(spaceLoc);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// otherwise just use the whole string without the command char.
|
||||
command = search.substr(1, search.length());
|
||||
}
|
||||
}
|
||||
|
||||
// Check for built in functions
|
||||
if (command == "help")
|
||||
{
|
||||
return help(userRef, args);
|
||||
}
|
||||
else if (command == "list")
|
||||
{
|
||||
list(userRef);
|
||||
return true;
|
||||
}
|
||||
|
||||
// If no built in functions match, search library
|
||||
auto com = m_commands.find(command);
|
||||
if (com != m_commands.end())
|
||||
{
|
||||
com->second.callback(args);
|
||||
return true;
|
||||
}
|
||||
// No commands match
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Commander::help(std::size_t userRef, const std::vector<std::string>& args)
|
||||
{
|
||||
if (args.empty())
|
||||
{
|
||||
m_iris->sendMessage(
|
||||
userRef, "Type /help [command] to learn more about a command "
|
||||
"\nType /list for a list of available commands\n");
|
||||
return true;
|
||||
}
|
||||
else if (args[0] == "help")
|
||||
{
|
||||
m_iris->sendMessage(
|
||||
userRef, "Type /help [command] to learn more about a command \n");
|
||||
return true;
|
||||
}
|
||||
else if (args[0] == "list")
|
||||
{
|
||||
m_iris->sendMessage(userRef, "Lists available commands\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
auto com = m_commands.find(args[0]);
|
||||
if (com != m_commands.end())
|
||||
{
|
||||
m_iris->sendMessage(userRef, com->second.help + "\n");
|
||||
return true;
|
||||
}
|
||||
m_iris->sendMessage(userRef, "Command \"" + args[0] + "\" not found \n");
|
||||
return false;
|
||||
}
|
||||
|
||||
void Commander::list(std::size_t userRef)
|
||||
{
|
||||
m_iris->sendMessage(userRef, "Available commands:\n");
|
||||
for (const auto& com : m_commands)
|
||||
{
|
||||
m_iris->sendMessage(userRef, "- " + com.second.command + "\n");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
// Copyright 2019-20 Genten Studios
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <Server/Game.hpp>
|
||||
#include <Server/User.hpp>
|
||||
|
||||
#include <Common/Actor.hpp>
|
||||
#include <Common/Movement.hpp>
|
||||
#include <Common/Position.hpp>
|
||||
#include <thread>
|
||||
|
||||
using namespace phx;
|
||||
using namespace phx::server;
|
||||
|
||||
Game::Game(entt::registry* registry, bool* running, net::Iris* iris)
|
||||
: m_registry(registry), m_running(running), m_iris(iris)
|
||||
{
|
||||
m_commander = new Commander(m_iris);
|
||||
}
|
||||
|
||||
void Game::registerAPI(cms::ModManager* manager)
|
||||
{
|
||||
m_commander->registerAPI(manager);
|
||||
}
|
||||
|
||||
void Game::run()
|
||||
{
|
||||
while (m_running)
|
||||
{
|
||||
// Process everybody's input first
|
||||
net::StateBundle m_currentState = m_iris->stateQueue.pop();
|
||||
|
||||
for (const auto& state : m_currentState.states)
|
||||
{
|
||||
ActorSystem::tick(m_registry,
|
||||
m_registry->get<Player>(state.first).actor, dt,
|
||||
state.second);
|
||||
|
||||
// @todo remove this debug statement before merging to develop
|
||||
// std::cout << m_registry
|
||||
// ->get<Position>(
|
||||
// m_registry->get<Player>(state.first).actor)
|
||||
// .position
|
||||
// << "\n";
|
||||
}
|
||||
// Process events second
|
||||
|
||||
// Process messages last
|
||||
size_t size = m_iris->messageQueue.size();
|
||||
for (size_t i = 0; i < size; i++)
|
||||
{
|
||||
net::MessageBundle message = m_iris->messageQueue.front();
|
||||
m_commander->run(message.userID, message.message);
|
||||
m_iris->messageQueue.pop();
|
||||
}
|
||||
|
||||
m_iris->sendState(m_registry, m_currentState.sequence);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,239 @@
|
|||
// Copyright 2019-20 Genten Studios
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
// may be used to endorse or promote products derived from this software without
|
||||
// specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <Server/Iris.hpp>
|
||||
#include <Server/User.hpp>
|
||||
|
||||
#include <Common/Actor.hpp>
|
||||
#include <Common/Logger.hpp>
|
||||
#include <Common/Movement.hpp>
|
||||
#include <Common/Position.hpp>
|
||||
#include <Common/Serialization/Serializer.hpp>
|
||||
|
||||
#include <cstring> //For std::memcpy on non-mac machines
|
||||
|
||||
using namespace phx;
|
||||
using namespace phx::net;
|
||||
using namespace phx::server::net;
|
||||
|
||||
/// @todo Replace this with the config system
|
||||
static const std::size_t MAX_USERS = 32;
|
||||
|
||||
Iris::Iris(entt::registry* registry) : m_registry(registry)
|
||||
{
|
||||
m_server = new phx::net::Host(phx::net::Address(7777), MAX_USERS, 3);
|
||||
|
||||
m_server->onConnect([this](Peer& peer, enet_uint32) {
|
||||
LOG_INFO("NETWORK")
|
||||
<< "Client connected from: " << peer.getAddress().getIP();
|
||||
{
|
||||
auto entity = m_registry->create();
|
||||
m_registry->emplace<Player>(entity,
|
||||
ActorSystem::registerActor(m_registry));
|
||||
m_users.emplace(peer.getID(), entity);
|
||||
}
|
||||
});
|
||||
|
||||
m_server->onReceive(
|
||||
[this](Peer& peer, Packet&& packet, enet_uint32 channelID) {
|
||||
switch (channelID)
|
||||
{
|
||||
case 0:
|
||||
parseEvent(peer.getID(), packet);
|
||||
break;
|
||||
case 1:
|
||||
parseState(peer.getID(), packet);
|
||||
break;
|
||||
case 2:
|
||||
parseMessage(peer.getID(), packet);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
m_server->onDisconnect(
|
||||
[this](std::size_t peerID, enet_uint32) { disconnect(peerID); });
|
||||
}
|
||||
|
||||
Iris::~Iris() { delete m_server; }
|
||||
|
||||
void Iris::run()
|
||||
{
|
||||
m_running = true;
|
||||
while (m_running)
|
||||
{
|
||||
m_server->poll();
|
||||
}
|
||||
}
|
||||
|
||||
void Iris::disconnect(std::size_t peerID)
|
||||
{
|
||||
LOG_INFO("NETWORK") << peerID << " disconnected";
|
||||
}
|
||||
|
||||
void Iris::parseEvent(std::size_t userID, Packet& packet)
|
||||
{
|
||||
std::string data;
|
||||
|
||||
phx::Serializer ser(Serializer::Mode::READ);
|
||||
ser.setBuffer(packet.getData());
|
||||
ser& data;
|
||||
|
||||
printf("Event received");
|
||||
printf("An Event packet containing %s was received from %lu\n",
|
||||
data.c_str(), userID);
|
||||
}
|
||||
|
||||
void Iris::parseState(std::size_t userID, phx::net::Packet& packet)
|
||||
{
|
||||
const float dt = 1.f / 20.f;
|
||||
|
||||
InputState input;
|
||||
|
||||
auto data = packet.getData();
|
||||
|
||||
phx::Serializer ser(Serializer::Mode::READ);
|
||||
ser.setBuffer(reinterpret_cast<std::byte*>(&data), packet.getSize());
|
||||
ser& input;
|
||||
|
||||
// If the queue is empty we need to add a new bundle
|
||||
if (currentBundles.empty())
|
||||
{
|
||||
StateBundle bundle;
|
||||
bundle.sequence = input.sequence;
|
||||
bundle.ready = false;
|
||||
bundle.users = 1; ///@todo We need to capture how many users we are
|
||||
/// expecting packets from
|
||||
currentBundles.push_back(bundle);
|
||||
}
|
||||
|
||||
// Discard state if its older that the oldest stateBundle
|
||||
if (input.sequence < currentBundles.front().sequence &&
|
||||
currentBundles.back().sequence - input.sequence < 10)
|
||||
{
|
||||
printf("discard %lu \n", input.sequence);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fill the stateBundles up to the current input sequence
|
||||
while ((input.sequence > currentBundles.back().sequence &&
|
||||
input.sequence - currentBundles.back().sequence > 10) ||
|
||||
currentBundles.back().sequence == 255)
|
||||
{
|
||||
// Insert a new bundle if this is the first packet in this sequence
|
||||
StateBundle bundle;
|
||||
bundle.sequence = currentBundles.back().sequence + 1;
|
||||
bundle.ready = false;
|
||||
bundle.users = 1; ///@todo We need to capture how many users we are
|
||||
/// expecting packets from
|
||||
currentBundles.push_back(bundle);
|
||||
}
|
||||
|
||||
{
|
||||
// printf("insert existing %lu \n", input.sequence);
|
||||
for (auto it = currentBundles.begin(); it != currentBundles.end(); ++it)
|
||||
{
|
||||
StateBundle& bundle = *it;
|
||||
if (bundle.sequence == input.sequence)
|
||||
{
|
||||
// Thread safety! If we said a bundle is ready, were too late
|
||||
if (!bundle.ready)
|
||||
{
|
||||
bundle.states[m_users[userID]] = input;
|
||||
// If we have all the states we need, then the bundle is
|
||||
// ready
|
||||
if (bundle.states.size() >= bundle.users)
|
||||
{
|
||||
bundle.ready = true;
|
||||
stateQueue.push(bundle);
|
||||
currentBundles.erase(it);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we have more than 10 states enqueued, assume we lost a packet
|
||||
if (currentBundles.size() > 10)
|
||||
{
|
||||
currentBundles.front().ready = true;
|
||||
stateQueue.push(currentBundles.front());
|
||||
currentBundles.erase(currentBundles.begin());
|
||||
}
|
||||
}
|
||||
|
||||
void Iris::parseMessage(std::size_t userID, phx::net::Packet& packet)
|
||||
{
|
||||
std::string input;
|
||||
|
||||
auto data = packet.getData();
|
||||
|
||||
phx::Serializer ser(Serializer::Mode::READ);
|
||||
ser.setBuffer(reinterpret_cast<std::byte*>(data.data()), data.size());
|
||||
ser& input;
|
||||
|
||||
/// @TODO replace userID with userName
|
||||
std::cout << userID << ": " << input << "\n";
|
||||
|
||||
if (input[0] == '/')
|
||||
{
|
||||
MessageBundle message;
|
||||
message.message = input.substr(1);
|
||||
message.userID = userID;
|
||||
messageQueue.push(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
sendMessage(userID, input);
|
||||
}
|
||||
}
|
||||
|
||||
void Iris::sendEvent(std::size_t userID, enet_uint8* data) {}
|
||||
|
||||
void Iris::sendState(entt::registry* registry, std::size_t sequence)
|
||||
{
|
||||
auto view = registry->view<Position, Movement>();
|
||||
Serializer ser(Serializer::Mode::WRITE);
|
||||
ser& sequence;
|
||||
for (auto entity : view)
|
||||
{
|
||||
auto pos = view.get<Position>(entity);
|
||||
ser& pos.position.x& pos.position.y& pos.position.z;
|
||||
}
|
||||
Packet packet = Packet(ser.getBuffer(), PacketFlags::UNRELIABLE);
|
||||
m_server->broadcast(packet, 1);
|
||||
}
|
||||
|
||||
void Iris::sendMessage(std::size_t userID, std::string message)
|
||||
{
|
||||
Serializer ser(Serializer::Mode::WRITE);
|
||||
ser& message;
|
||||
Packet packet = Packet(ser.getBuffer(), PacketFlags::RELIABLE);
|
||||
Peer* peer = m_server->getPeer(userID);
|
||||
peer->send(packet, 2);
|
||||
}
|
|
@ -33,9 +33,15 @@ using namespace phx;
|
|||
#undef main
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
server::Server* server = new server::Server();
|
||||
// std::string save;
|
||||
// if (argc > 0){
|
||||
// save = argv[0];
|
||||
// } else {
|
||||
// save = "save1";
|
||||
// }
|
||||
|
||||
server::Server* server = new server::Server("save1");
|
||||
server->run();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2019-20 Genten Studios
|
||||
// Copyright 2020 Genten Studios
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
|
@ -27,25 +27,135 @@
|
|||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <Server/Server.hpp>
|
||||
|
||||
#include <Common/Voxels/BlockRegistry.hpp>
|
||||
|
||||
#include <Common/Logger.hpp>
|
||||
#include <Common/Settings.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
using namespace phx::server;
|
||||
using namespace phx;
|
||||
|
||||
void Server::run() {
|
||||
std::cout << "Hello, Server!\nType \"exit\" to exit" << std::endl;
|
||||
Settings::get()->load("config.txt");
|
||||
m_running = true;
|
||||
while(m_running == true){
|
||||
std::cout << "\n>";
|
||||
std::string input;
|
||||
std::cin >> input;
|
||||
if (input == "exit"){
|
||||
m_running = false;
|
||||
}
|
||||
std::cout << input;
|
||||
}
|
||||
Settings::get()->save("config.txt");
|
||||
}
|
||||
Server::Server(std::string save) : m_save(std::move(save))
|
||||
{
|
||||
m_iris = new server::net::Iris(&m_registry);
|
||||
m_game = new Game(&m_registry, &m_running, m_iris);
|
||||
}
|
||||
|
||||
void registerUnusedAPI(cms::ModManager* manager)
|
||||
{
|
||||
manager->registerFunction("core.input.registerInput",
|
||||
[](std::string uniqueName,
|
||||
std::string displayName,
|
||||
std::string defaultKey) {});
|
||||
manager->registerFunction("core.input.getInput", [](int input) {});
|
||||
manager->registerFunction("core.input.getInputRef",
|
||||
[](std::string uniqueName) {});
|
||||
manager->registerFunction("core.input.registerCallback",
|
||||
[](int input, sol::function f) {});
|
||||
manager->registerFunction(
|
||||
"audio.loadMP3",
|
||||
[=](const std::string& uniqueName, const std::string& filePath) {});
|
||||
manager->registerFunction("audio.play", [=](sol::table source) {});
|
||||
}
|
||||
|
||||
void Server::run()
|
||||
{
|
||||
std::cout << "Hello, Server!" << std::endl;
|
||||
|
||||
Logger::get()->initialize({});
|
||||
Settings::get()->load("config.txt");
|
||||
|
||||
// Initialize the Modules //
|
||||
|
||||
std::fstream fileStream;
|
||||
std::vector<std::string> toLoad;
|
||||
|
||||
fileStream.open("Saves/" + m_save + "/Mods.txt");
|
||||
if (!fileStream.is_open())
|
||||
{
|
||||
LOG_FATAL("CMS") << "Error opening save file: \"Saves/" + m_save +
|
||||
"/Mods.txt\"";
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
std::string modules;
|
||||
while (std::getline(fileStream, modules))
|
||||
{
|
||||
toLoad.push_back(modules);
|
||||
}
|
||||
|
||||
m_modManager = new cms::ModManager(toLoad, {"Modules"});
|
||||
|
||||
m_modManager->registerFunction("core.print", [=](const std::string& text) {
|
||||
std::cout << text << "\n";
|
||||
});
|
||||
|
||||
voxels::BlockRegistry::get()->registerAPI(m_modManager);
|
||||
Settings::get()->registerAPI(m_modManager);
|
||||
m_game->registerAPI(m_modManager);
|
||||
|
||||
m_modManager->registerFunction("core.log_warning", [](std::string message) {
|
||||
LOG_WARNING("MODULE") << message;
|
||||
});
|
||||
m_modManager->registerFunction("core.log_fatal", [](std::string message) {
|
||||
LOG_FATAL("MODULE") << message;
|
||||
});
|
||||
m_modManager->registerFunction("core.log_info", [](std::string message) {
|
||||
LOG_INFO("MODULE") << message;
|
||||
});
|
||||
m_modManager->registerFunction("core.log_debug", [](std::string message) {
|
||||
LOG_DEBUG("MODULE") << message;
|
||||
});
|
||||
|
||||
registerUnusedAPI(m_modManager);
|
||||
|
||||
float progress = 0.f;
|
||||
auto result = m_modManager->load(&progress);
|
||||
|
||||
if (!result.ok)
|
||||
{
|
||||
LOG_FATAL("CMS") << "An error has occurred loading modules.";
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Modules Initialized //
|
||||
|
||||
// Fire up Threads //
|
||||
|
||||
m_running = true;
|
||||
|
||||
std::thread t_iris(&server::net::Iris::run, m_iris);
|
||||
std::thread t_game(&Game::run, m_game);
|
||||
|
||||
// Enter Main Loop //
|
||||
|
||||
std::string input;
|
||||
while (m_running)
|
||||
{
|
||||
/// @todo Replace simple loop with commander
|
||||
std::cin >> input;
|
||||
if (input == "q")
|
||||
{
|
||||
m_running = false;
|
||||
m_iris->kill();
|
||||
}
|
||||
}
|
||||
|
||||
// Begin Shutdown //
|
||||
|
||||
t_iris.join();
|
||||
t_game.join();
|
||||
Settings::get()->save("config.txt");
|
||||
}
|
||||
|
||||
Server::~Server()
|
||||
{
|
||||
delete m_iris;
|
||||
delete m_game;
|
||||
delete m_modManager;
|
||||
}
|
||||
|
|
|
@ -15,21 +15,29 @@ set(ALSOFT_NO_CONFIG_UTIL ON)
|
|||
set(ALSOFT_EXAMPLES OFF)
|
||||
set(ALSOFT_TESTS OFF)
|
||||
|
||||
# turn off the EnTT tests.
|
||||
set(BUILD_TESTING OFF)
|
||||
|
||||
# define for EnTT to work
|
||||
add_definitions(-DNOMINMAX)
|
||||
|
||||
add_subdirectory (openal-soft)
|
||||
add_subdirectory (SDL2)
|
||||
add_subdirectory (Glad)
|
||||
add_subdirectory (ImGui)
|
||||
add_subdirectory (sol2)
|
||||
add_subdirectory (lua)
|
||||
add_subdirectory (enet)
|
||||
add_subdirectory (entt)
|
||||
add_subdirectory (json)
|
||||
|
||||
# new line for each group of dependencies. Currently SDL, OpenGL, Lua and then OpenAL.
|
||||
set_target_properties(SDL2main SDL2-static uninstall
|
||||
Glad ImGui
|
||||
lua liblua
|
||||
build_version native-tools
|
||||
OpenAL common ex-common
|
||||
PROPERTIES FOLDER Dependencies)
|
||||
Glad ImGui
|
||||
lua liblua
|
||||
enet aob build_version native-tools
|
||||
OpenAL common ex-common
|
||||
PROPERTIES FOLDER Dependencies)
|
||||
|
||||
# utility thing to make phoenix cmake tidier
|
||||
set(PHX_THIRD_PARTY_INCLUDES
|
||||
|
@ -38,6 +46,7 @@ set(PHX_THIRD_PARTY_INCLUDES
|
|||
${CMAKE_CURRENT_LIST_DIR}/ImGui
|
||||
${CMAKE_CURRENT_LIST_DIR}/ImGui/examples
|
||||
${CMAKE_CURRENT_LIST_DIR}/lua
|
||||
${CMAKE_CURRENT_LIST_DIR}/enet/include
|
||||
${CMAKE_CURRENT_LIST_DIR}/sol2/include
|
||||
${CMAKE_CURRENT_LIST_DIR}/stb_image
|
||||
${CMAKE_CURRENT_LIST_DIR}/stb_rectpack
|
||||
|
@ -54,11 +63,14 @@ set(PHX_THIRD_PARTY_LIBRARIES
|
|||
SDL2main
|
||||
Glad
|
||||
ImGui
|
||||
enet
|
||||
sol2
|
||||
liblua
|
||||
nlohmann_json::nlohmann_json
|
||||
OpenAL
|
||||
$<$<PLATFORM_ID:Windows>:opengl32.lib> # link to opengl32.lib if windows.
|
||||
$<$<PLATFORM_ID:Windows>:ws2_32.lib> # link to ws2_32.lib if windows.
|
||||
$<$<PLATFORM_ID:Windows>:winmm.lib> # link to winmm.lib if windows.
|
||||
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 6537dc81f36ff9c8d5c5a0f53950e4d39b374475
|
Loading…
Reference in New Issue