Compare commits
43 Commits
5ec6ff42ab
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 65a84ecab0 | |||
| e193d05417 | |||
| 82c7ff4895 | |||
| c9d94febbc | |||
| 15625e5c30 | |||
| 89a6219072 | |||
| 2df849b63e | |||
| e0d4dd053f | |||
| 7b693c7d43 | |||
| de6742f64d | |||
| adb6dce08a | |||
| af3ac8d37c | |||
| 282ddac80c | |||
| 2065ea8334 | |||
| b6530c1220 | |||
| c14234551d | |||
| 5d7add69aa | |||
| 632a23c084 | |||
| 5a16af2696 | |||
| acbc00c6c6 | |||
| d5d459b658 | |||
| ff73cd348e | |||
| 89d5ad5f54 | |||
| 392eaeab83 | |||
| cbb2f5005e | |||
| 92a2e53036 | |||
| 36a2e67ac4 | |||
| a789cf9b1a | |||
| f165aec824 | |||
| 810e17cc39 | |||
| d412871de5 | |||
| 072c13ca89 | |||
| 998dc7ea2e | |||
| 3eb3eadd4b | |||
| 159867e747 | |||
| f9f5f60049 | |||
| 7abb31c2e1 | |||
| 8bc2f26578 | |||
| 9fadb86031 | |||
| 9f8dc0630d | |||
| c192c73d34 | |||
| 6e9c747b2d | |||
| 2e63a474b8 |
37
.clang-format
Normal file
37
.clang-format
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
Language: Cpp
|
||||||
|
BasedOnStyle: LLVM
|
||||||
|
|
||||||
|
AlignAfterOpenBracket: DontAlign
|
||||||
|
BreakConstructorInitializers: AfterColon
|
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||||
|
PointerAlignment: Left
|
||||||
|
SortIncludes: true
|
||||||
|
SpacesBeforeTrailingComments: 2
|
||||||
|
UseTab: Always
|
||||||
|
MaxEmptyLinesToKeep: 5
|
||||||
|
|
||||||
|
TabWidth: 4
|
||||||
|
ConstructorInitializerIndentWidth: 4
|
||||||
|
ContinuationIndentWidth: 4
|
||||||
|
IndentWidth: 4
|
||||||
|
IndentCaseLabels: true
|
||||||
|
|
||||||
|
ColumnLimit: 135
|
||||||
|
AlwaysBreakTemplateDeclarations: Yes
|
||||||
|
|
||||||
|
AllowShortFunctionsOnASingleLine: Empty
|
||||||
|
BreakBeforeBraces: Custom
|
||||||
|
BraceWrapping:
|
||||||
|
AfterClass: false
|
||||||
|
AfterControlStatement: false
|
||||||
|
AfterEnum: false
|
||||||
|
AfterExternBlock: false
|
||||||
|
AfterFunction: false
|
||||||
|
AfterNamespace: false
|
||||||
|
AfterStruct: false
|
||||||
|
AfterUnion: false
|
||||||
|
BeforeCatch: false
|
||||||
|
BeforeElse: false
|
||||||
|
IndentBraces: false
|
||||||
|
SplitEmptyFunction: true
|
||||||
|
SplitEmptyRecord: true
|
||||||
52
.gitea/workflows/linux.yaml
Normal file
52
.gitea/workflows/linux.yaml
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
name: Linux arm64
|
||||||
|
run-name: Build And Test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
Build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
XMAKE_ROOT: y
|
||||||
|
steps:
|
||||||
|
- name: Check out repository code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Prepare Xmake
|
||||||
|
uses: xmake-io/github-action-setup-xmake@v1
|
||||||
|
with:
|
||||||
|
xmake-version: latest
|
||||||
|
actions-cache-folder: '.xmake-cache'
|
||||||
|
actions-cache-key: 'ubuntu-xmake'
|
||||||
|
|
||||||
|
- name: Packages List
|
||||||
|
run : |
|
||||||
|
xmake show -l packages | grep -E 'require|project' > ${{ github.workspace }}/packages.txt
|
||||||
|
cat ${{ github.workspace }}/packages.txt
|
||||||
|
|
||||||
|
- name: Calc deps hash
|
||||||
|
uses: seepine/hash-files@v1
|
||||||
|
id: get-hash
|
||||||
|
with:
|
||||||
|
patterns: 'packages.txt'
|
||||||
|
|
||||||
|
- name: Packages cache
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ~/.xmake/packages
|
||||||
|
key: ${{ runner.os }}-${{ steps.get-hash.outputs.hash }}
|
||||||
|
|
||||||
|
- name: XMake config
|
||||||
|
run: xmake f --policies=package.install_only -p linux -y
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: xmake
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: xmake test
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -9,3 +9,4 @@ NazaraLog.log
|
|||||||
|
|
||||||
.vscode/settings.json
|
.vscode/settings.json
|
||||||
.vscode/compile_commands.json
|
.vscode/compile_commands.json
|
||||||
|
.cache
|
||||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "doxygen/doxygen-awesome-css"]
|
||||||
|
path = doxygen/doxygen-awesome-css
|
||||||
|
url = https://github.com/jothepro/doxygen-awesome-css.git
|
||||||
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@@ -8,7 +8,7 @@
|
|||||||
"type": "xmake",
|
"type": "xmake",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"name": "Debug",
|
"name": "Debug",
|
||||||
"target": "Blitz2",
|
"target": "Blitz2Client",
|
||||||
"cwd": "${workspaceFolder}",
|
"cwd": "${workspaceFolder}",
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
58
assets/example.passlist
Normal file
58
assets/example.passlist
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
passlist "Forward Passlist"
|
||||||
|
{
|
||||||
|
attachment "DepthBuffer"
|
||||||
|
{
|
||||||
|
format "PreferredDepthStencil"
|
||||||
|
}
|
||||||
|
|
||||||
|
pass "DepthPrepass"
|
||||||
|
{
|
||||||
|
impl "Depth"
|
||||||
|
{
|
||||||
|
MatPass "DepthPass"
|
||||||
|
}
|
||||||
|
|
||||||
|
depthstenciloutput "DepthBuffer"
|
||||||
|
}
|
||||||
|
|
||||||
|
attachment "ForwardOutput"
|
||||||
|
{
|
||||||
|
format "RGBA16F"
|
||||||
|
}
|
||||||
|
|
||||||
|
pass "ForwardPass"
|
||||||
|
{
|
||||||
|
impl "Forward"
|
||||||
|
output "Output" "ForwardOutput"
|
||||||
|
depthstencilinput "DepthBuffer"
|
||||||
|
depthstenciloutput "DepthBuffer"
|
||||||
|
flag "LightShadowing"
|
||||||
|
}
|
||||||
|
|
||||||
|
attachment "Gamma"
|
||||||
|
{
|
||||||
|
format "RGBA8"
|
||||||
|
}
|
||||||
|
|
||||||
|
pass "Gamma correction"
|
||||||
|
{
|
||||||
|
impl "PostProcess"
|
||||||
|
{
|
||||||
|
Shader "PostProcess.GammaCorrection"
|
||||||
|
}
|
||||||
|
|
||||||
|
input "Input" "ForwardOutput"
|
||||||
|
output "Output" "Gamma"
|
||||||
|
}
|
||||||
|
|
||||||
|
attachmentproxy "ImguiOutput" "Gamma"
|
||||||
|
|
||||||
|
pass "Imgui"
|
||||||
|
{
|
||||||
|
impl "Imgui"
|
||||||
|
input "Input" "Gamma"
|
||||||
|
output "Output" "ImguiOutput"
|
||||||
|
}
|
||||||
|
|
||||||
|
output "ImguiOutput"
|
||||||
|
}
|
||||||
BIN
assets/fonts/doom.ttf
Normal file
BIN
assets/fonts/doom.ttf
Normal file
Binary file not shown.
1
doxygen/doxygen-awesome-css
Submodule
1
doxygen/doxygen-awesome-css
Submodule
Submodule doxygen/doxygen-awesome-css added at 40e9b25b61
21
doxygen/footer.html
Normal file
21
doxygen/footer.html
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<!-- HTML footer for doxygen 1.9.1-->
|
||||||
|
<!-- start footer part -->
|
||||||
|
<!--BEGIN GENERATE_TREEVIEW-->
|
||||||
|
<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
|
||||||
|
<ul>
|
||||||
|
$navpath
|
||||||
|
<li class="footer">$generatedby <a href="https://www.doxygen.org/index.html"><img class="footer"
|
||||||
|
src="$relpath^doxygen.svg" width="104" height="31" alt="doxygen" /></a> $doxygenversion </li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<!--END GENERATE_TREEVIEW-->
|
||||||
|
<!--BEGIN !GENERATE_TREEVIEW-->
|
||||||
|
<hr class="footer" />
|
||||||
|
<address class="footer"><small>
|
||||||
|
$generatedby <a href="https://www.doxygen.org/index.html"><img class="footer" src="$relpath^doxygen.svg"
|
||||||
|
width="104" height="31" alt="doxygen" /></a> $doxygenversion
|
||||||
|
</small></address>
|
||||||
|
<!--END !GENERATE_TREEVIEW-->
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
66
doxygen/header.html
Normal file
66
doxygen/header.html
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
<!-- HTML header for doxygen 1.9.1-->
|
||||||
|
<!DOCTYPE html
|
||||||
|
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=9" />
|
||||||
|
<meta name="generator" content="Doxygen $doxygenversion" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<!--BEGIN PROJECT_NAME-->
|
||||||
|
<title>$projectname: $title</title><!--END PROJECT_NAME-->
|
||||||
|
<!--BEGIN !PROJECT_NAME-->
|
||||||
|
<title>$title</title><!--END !PROJECT_NAME-->
|
||||||
|
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css" />
|
||||||
|
<script type="text/javascript" src="$relpath^jquery.js"></script>
|
||||||
|
<script type="text/javascript" src="$relpath^dynsections.js"></script>
|
||||||
|
$treeview
|
||||||
|
$search
|
||||||
|
$mathjax
|
||||||
|
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
|
||||||
|
$extrastylesheet
|
||||||
|
<script type="text/javascript" src="$relpath^doxygen-awesome-darkmode-toggle.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
DoxygenAwesomeDarkModeToggle.init()
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
|
||||||
|
|
||||||
|
<!--BEGIN TITLEAREA-->
|
||||||
|
<div id="titlearea">
|
||||||
|
<table cellspacing="0" cellpadding="0">
|
||||||
|
<tbody>
|
||||||
|
<tr style="height: 56px;">
|
||||||
|
<!--BEGIN PROJECT_LOGO-->
|
||||||
|
<td id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo" /></td>
|
||||||
|
<!--END PROJECT_LOGO-->
|
||||||
|
<!--BEGIN PROJECT_NAME-->
|
||||||
|
<td id="projectalign" style="padding-left: 0.5em;">
|
||||||
|
<div id="projectname">$projectname
|
||||||
|
<!--BEGIN PROJECT_NUMBER--> <span id="projectnumber">$projectnumber</span><!--END PROJECT_NUMBER-->
|
||||||
|
</div>
|
||||||
|
<!--BEGIN PROJECT_BRIEF-->
|
||||||
|
<div id="projectbrief">$projectbrief</div><!--END PROJECT_BRIEF-->
|
||||||
|
</td>
|
||||||
|
<!--END PROJECT_NAME-->
|
||||||
|
<!--BEGIN !PROJECT_NAME-->
|
||||||
|
<!--BEGIN PROJECT_BRIEF-->
|
||||||
|
<td style="padding-left: 0.5em;">
|
||||||
|
<div id="projectbrief">$projectbrief</div>
|
||||||
|
</td>
|
||||||
|
<!--END PROJECT_BRIEF-->
|
||||||
|
<!--END !PROJECT_NAME-->
|
||||||
|
<!--BEGIN DISABLE_INDEX-->
|
||||||
|
<!--BEGIN SEARCHENGINE-->
|
||||||
|
<td>$searchbox</td>
|
||||||
|
<!--END SEARCHENGINE-->
|
||||||
|
<!--END DISABLE_INDEX-->
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<!--END TITLEAREA-->
|
||||||
|
<!-- end header part -->
|
||||||
5
doxygen/stylesheet.css
Normal file
5
doxygen/stylesheet.css
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
/* The standard CSS for doxygen 1.9.1 */
|
||||||
|
|
||||||
|
body, table, div, p, dl {
|
||||||
|
font: 400 14px/22px Roboto,sans-serif;
|
||||||
|
}
|
||||||
36
include/blitz/common/Format.h
Normal file
36
include/blitz/common/Format.h
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file Format.h
|
||||||
|
* \brief This file contains the definition of the `Format` function.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Formats a string using a format string and variadic arguments.
|
||||||
|
*
|
||||||
|
* This function takes a format string and a variable number of arguments and returns a formatted string.
|
||||||
|
* The format string can contain placeholders that will be replaced by the corresponding arguments.
|
||||||
|
*
|
||||||
|
* \param format The format string.
|
||||||
|
* \param args The variadic arguments to be formatted.
|
||||||
|
* \return The formatted string.
|
||||||
|
* \throws std::runtime_error if an error occurs during formatting.
|
||||||
|
*/
|
||||||
|
template <typename... Args>
|
||||||
|
std::string Format(const std::string& format, Args... args) {
|
||||||
|
int size = snprintf(nullptr, 0, format.c_str(), args...) + 1; // Extra space for '\0'
|
||||||
|
if (size <= 0) {
|
||||||
|
throw std::runtime_error("Error during formatting.");
|
||||||
|
}
|
||||||
|
std::unique_ptr<char[]> buf(new char[size]);
|
||||||
|
snprintf(buf.get(), static_cast<std::size_t>(size), format.c_str(), args...);
|
||||||
|
return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace blitz
|
||||||
30
include/blitz/common/Log.h
Normal file
30
include/blitz/common/Log.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file Log.h
|
||||||
|
* \brief File defining log functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Logs a normal message.
|
||||||
|
* \param msg The message to be logged.
|
||||||
|
*/
|
||||||
|
void Log(const std::string& msg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Logs a normal message in debug mode.
|
||||||
|
* \param msg The message to be logged.
|
||||||
|
*/
|
||||||
|
void LogD(const std::string& msg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Logs an error message.
|
||||||
|
* \param err The error message to be logged.
|
||||||
|
*/
|
||||||
|
void LogE(const std::string& err);
|
||||||
|
|
||||||
|
} // namespace blitz
|
||||||
25
include/blitz/common/NonCopyable.h
Normal file
25
include/blitz/common/NonCopyable.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file NonCopyable.h
|
||||||
|
* \brief File containing the blitz::NonCopyable class
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \class NonCopyable
|
||||||
|
* \brief Class used to make a class non copyable
|
||||||
|
* \note Inherit from this class privately to make a class non copyable
|
||||||
|
*/
|
||||||
|
class NonCopyable {
|
||||||
|
public:
|
||||||
|
NonCopyable(const NonCopyable&) = delete;
|
||||||
|
NonCopyable& operator=(const NonCopyable&) = delete;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
NonCopyable() {}
|
||||||
|
~NonCopyable() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace blitz
|
||||||
31
include/blitz/common/Types.h
Normal file
31
include/blitz/common/Types.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Nazara/Core/EnttWorld.hpp>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
|
||||||
|
struct AtomicEnttWorld {
|
||||||
|
Nz::EnttWorld& m_World;
|
||||||
|
std::lock_guard<std::mutex> m_LockGuard;
|
||||||
|
|
||||||
|
Nz::EnttWorld* operator->() {
|
||||||
|
return &m_World;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EnttWorld {
|
||||||
|
Nz::EnttWorld& m_World;
|
||||||
|
std::mutex m_Mutex;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \return an AtomicEnttWorld structure which will lock the associated mutex until destruction
|
||||||
|
* \warning Do not hold more than one instance or the current thread will self lock
|
||||||
|
*/
|
||||||
|
operator AtomicEnttWorld() {
|
||||||
|
return {m_World, std::lock_guard<std::mutex>(m_Mutex)};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using EntityID = std::uint32_t;
|
||||||
|
|
||||||
|
} // namespace blitz
|
||||||
13
include/blitz/components/PlayerInfo.h
Normal file
13
include/blitz/components/PlayerInfo.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <blitz/common/Types.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
|
||||||
|
struct PlayerInfoComponent {
|
||||||
|
EntityID m_PlayerId;
|
||||||
|
std::string m_Pseudo;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace blitz
|
||||||
9
include/blitz/components/PlayerRemove.h
Normal file
9
include/blitz/components/PlayerRemove.h
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <blitz/common/Types.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
|
||||||
|
struct PlayerRemoveComponent {};
|
||||||
|
|
||||||
|
} // namespace blitz
|
||||||
37
include/blitz/network/EnetClient.h
Normal file
37
include/blitz/network/EnetClient.h
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Nazara/Network/ENetHost.hpp>
|
||||||
|
#include <blitz/network/EnetConnection.h>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace network {
|
||||||
|
|
||||||
|
class EnetClient : private NonCopyable {
|
||||||
|
public:
|
||||||
|
EnetClient(const Nz::IpAddress& address);
|
||||||
|
~EnetClient();
|
||||||
|
|
||||||
|
void Disconnect();
|
||||||
|
|
||||||
|
NazaraSignal(OnConnect);
|
||||||
|
NazaraSignal(OnDisconnect);
|
||||||
|
NazaraSignal(OnDisconnectTimeout);
|
||||||
|
|
||||||
|
EnetConnection& GetConnection() {
|
||||||
|
return m_Connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
EnetConnection m_Connection;
|
||||||
|
Nz::ENetHost m_Host;
|
||||||
|
Nz::ENetPeer* m_Peer;
|
||||||
|
std::jthread m_Thread;
|
||||||
|
bool m_Running;
|
||||||
|
|
||||||
|
void Update();
|
||||||
|
void WorkerThread();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace network
|
||||||
|
} // namespace blitz
|
||||||
61
include/blitz/network/EnetConnection.h
Normal file
61
include/blitz/network/EnetConnection.h
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file EnetConnection.h
|
||||||
|
* \brief File containing the blitz::network::EnetConnection class
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Nazara/Core/ByteArray.hpp>
|
||||||
|
#include <NazaraUtils/Signal.hpp>
|
||||||
|
#include <blitz/common/NonCopyable.h>
|
||||||
|
#include <blitz/protocol/PacketData.h>
|
||||||
|
#include <blitz/protocol/PacketDeclare.h>
|
||||||
|
|
||||||
|
namespace Nz {
|
||||||
|
|
||||||
|
class ENetPeer;
|
||||||
|
|
||||||
|
} // namespace Nz
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace network {
|
||||||
|
|
||||||
|
class EnetClient;
|
||||||
|
class EnetServer;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#define DeclarePacket(PacketName, ...) \
|
||||||
|
/** Sends a PacketName over the network */ \
|
||||||
|
void Send##PacketName(const blitz::protocol::data::PacketName& a_##PacketName) const; \
|
||||||
|
/** Use On##PacketName.Connect() to process a PacketName incoming from network */ \
|
||||||
|
NazaraSignal(On##PacketName, const blitz::protocol::data::PacketName&);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class EnetConnection : private NonCopyable {
|
||||||
|
public:
|
||||||
|
EnetConnection(Nz::ENetPeer* a_Peer = nullptr);
|
||||||
|
|
||||||
|
bool IsConnected() const;
|
||||||
|
|
||||||
|
std::uint16_t GetPeerId() const;
|
||||||
|
|
||||||
|
DeclareAllPacket();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Nz::ENetPeer* m_Peer;
|
||||||
|
|
||||||
|
void Recieve(Nz::ByteArray&);
|
||||||
|
void SetPeer(Nz::ENetPeer* a_Peer);
|
||||||
|
|
||||||
|
friend class EnetClient;
|
||||||
|
friend class EnetServer;
|
||||||
|
};
|
||||||
|
|
||||||
|
#undef DeclarePacket
|
||||||
|
|
||||||
|
} // namespace network
|
||||||
|
} // namespace blitz
|
||||||
43
include/blitz/network/EnetServer.h
Normal file
43
include/blitz/network/EnetServer.h
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Nazara/Core/ThreadExt.hpp>
|
||||||
|
#include <Nazara/Network/ENetHost.hpp>
|
||||||
|
#include <blitz/network/EnetConnection.h>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <map>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace network {
|
||||||
|
|
||||||
|
class EnetServer : private NonCopyable {
|
||||||
|
public:
|
||||||
|
EnetServer(std::uint16_t port);
|
||||||
|
~EnetServer();
|
||||||
|
|
||||||
|
void Destroy();
|
||||||
|
|
||||||
|
bool IsClosed() const;
|
||||||
|
|
||||||
|
void CloseConnection(std::uint16_t a_PeerId);
|
||||||
|
|
||||||
|
EnetConnection* GetConnection(std::uint16_t a_PeerId);
|
||||||
|
|
||||||
|
NazaraSignal(OnClientConnect, EnetConnection& /*a_Peer*/);
|
||||||
|
NazaraSignal(OnClientDisconnect, EnetConnection& /*a_Peer*/);
|
||||||
|
NazaraSignal(OnClientDisconnectTimeout, EnetConnection& /*a_Peer*/);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Update();
|
||||||
|
void WorkerThread();
|
||||||
|
|
||||||
|
void RemoveConnection(std::uint16_t a_PeerId);
|
||||||
|
|
||||||
|
Nz::ENetHost m_Host;
|
||||||
|
bool m_Running;
|
||||||
|
std::jthread m_Thread;
|
||||||
|
std::map<std::uint16_t, std::unique_ptr<EnetConnection>> m_Connections;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace network
|
||||||
|
} // namespace blitz
|
||||||
60
include/blitz/protocol/PacketData.h
Normal file
60
include/blitz/protocol/PacketData.h
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <blitz/components/PlayerInfo.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace protocol {
|
||||||
|
namespace data {
|
||||||
|
|
||||||
|
struct PlayerLogin {
|
||||||
|
std::string m_PlayerName;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UpdateHealth {
|
||||||
|
float m_NewHealth;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LoggingSuccess {
|
||||||
|
EntityID m_PlayerId;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PlayerDeath {};
|
||||||
|
|
||||||
|
struct PlayerJoin {
|
||||||
|
PlayerInfoComponent m_Player;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PlayerLeave {
|
||||||
|
EntityID m_PlayerId;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PlayerStats {};
|
||||||
|
|
||||||
|
struct PlayerList {
|
||||||
|
std::vector<PlayerInfoComponent> m_Players;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ServerConfig {};
|
||||||
|
|
||||||
|
struct ServerTps {};
|
||||||
|
|
||||||
|
struct UpdateGameState {};
|
||||||
|
|
||||||
|
struct KeepAlive {
|
||||||
|
std::uint64_t m_KeepAliveId;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Disconnect {};
|
||||||
|
|
||||||
|
struct ChatMessage {
|
||||||
|
std::string m_Text;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PlayerPositionAndRotation {};
|
||||||
|
|
||||||
|
struct PlayerShoot {};
|
||||||
|
|
||||||
|
} // namespace data
|
||||||
|
} // namespace protocol
|
||||||
|
} // namespace blitz
|
||||||
49
include/blitz/protocol/PacketDeclare.h
Normal file
49
include/blitz/protocol/PacketDeclare.h
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file PacketDeclare.h
|
||||||
|
* \brief Holds the definitions of the packets (but not their content)
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace protocol {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \enum PacketSender
|
||||||
|
* \brief Indicate who should send a packet
|
||||||
|
*/
|
||||||
|
enum class PacketSender {
|
||||||
|
/** Sent by clients and server */
|
||||||
|
Both,
|
||||||
|
/** Sent by clients to the server */
|
||||||
|
Client,
|
||||||
|
/** Sent by server to the clients */
|
||||||
|
Server,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \def DeclareAllPacket
|
||||||
|
* \brief Avoids repetitive operations on packets
|
||||||
|
*/
|
||||||
|
#define DeclareAllPacket() \
|
||||||
|
DeclarePacket(ChatMessage, Reliable, Both) \
|
||||||
|
DeclarePacket(Disconnect, Reliable, Both) \
|
||||||
|
DeclarePacket(KeepAlive, Reliable, Both) \
|
||||||
|
DeclarePacket(LoggingSuccess, Reliable, Server) \
|
||||||
|
DeclarePacket(PlayerDeath, Reliable, Server) \
|
||||||
|
DeclarePacket(PlayerJoin, Reliable, Server) \
|
||||||
|
DeclarePacket(PlayerLeave, Reliable, Server) \
|
||||||
|
DeclarePacket(PlayerList, Reliable, Server) \
|
||||||
|
DeclarePacket(PlayerLogin, Reliable, Client) \
|
||||||
|
DeclarePacket(PlayerPositionAndRotation, Reliable, Both) \
|
||||||
|
DeclarePacket(PlayerShoot, Reliable, Both) \
|
||||||
|
DeclarePacket(PlayerStats, Reliable, Server) \
|
||||||
|
DeclarePacket(ServerConfig, Reliable, Server) \
|
||||||
|
DeclarePacket(ServerTps, Reliable, Server) \
|
||||||
|
DeclarePacket(UpdateGameState, Reliable, Server) \
|
||||||
|
DeclarePacket(UpdateHealth, Reliable, Client)
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace protocol
|
||||||
|
} // namespace blitz
|
||||||
19
include/blitz/protocol/PacketFactory.h
Normal file
19
include/blitz/protocol/PacketFactory.h
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <blitz/protocol/Packets.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace protocol {
|
||||||
|
namespace PacketFactory {
|
||||||
|
|
||||||
|
template<typename PacketDerived, typename = typename std::enable_if<std::is_base_of<Packet, PacketDerived>::value>::type>
|
||||||
|
std::unique_ptr<PacketDerived> CreatePacket() {
|
||||||
|
return std::make_unique<PacketDerived>();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::unique_ptr<Packet>& CreateReadOnlyPacket(PacketType a_Type);
|
||||||
|
|
||||||
|
} // namespace PacketFactory
|
||||||
|
} // namespace protocol
|
||||||
|
} // namespace blitz
|
||||||
20
include/blitz/protocol/PacketHandler.h
Normal file
20
include/blitz/protocol/PacketHandler.h
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Nazara/Core/EnttWorld.hpp>
|
||||||
|
#include <blitz/network/EnetConnection.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace protocol {
|
||||||
|
|
||||||
|
class PacketHandler : private NonCopyable {
|
||||||
|
public:
|
||||||
|
PacketHandler(network::EnetConnection& a_Connection, EnttWorld& a_World);
|
||||||
|
virtual ~PacketHandler();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
network::EnetConnection& m_Connection;
|
||||||
|
EnttWorld& m_World;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace protocol
|
||||||
|
} // namespace blitz
|
||||||
19
include/blitz/protocol/PacketSerializer.h
Normal file
19
include/blitz/protocol/PacketSerializer.h
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <blitz/protocol/Packets.h>
|
||||||
|
#include <Nazara/Core/ByteArray.hpp>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace protocol {
|
||||||
|
|
||||||
|
using PacketPtr = std::unique_ptr<Packet>;
|
||||||
|
|
||||||
|
namespace PacketSerializer {
|
||||||
|
|
||||||
|
Nz::ByteArray Serialize(const Packet& a_Packet);
|
||||||
|
|
||||||
|
std::unique_ptr<Packet> Deserialize(Nz::ByteArray& a_Data);
|
||||||
|
|
||||||
|
}
|
||||||
|
} // namespace protocol
|
||||||
|
} // namespace blitz
|
||||||
39
include/blitz/protocol/PacketVisitor.h
Normal file
39
include/blitz/protocol/PacketVisitor.h
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file PacketVisitor.h
|
||||||
|
* \brief File containing the blitz::protocol::PacketVisitor class
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <blitz/protocol/Packets.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace protocol {
|
||||||
|
|
||||||
|
#define DeclarePacket(PacketName, ...) \
|
||||||
|
/** This function is called when the packet processed by PacketVisitor::Check is a PacketName */ \
|
||||||
|
virtual void Visit(const packets::PacketName&) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \class PacketVisitor
|
||||||
|
* \brief This class uses double-dispatch in order to find the real type of a packet
|
||||||
|
*/
|
||||||
|
class PacketVisitor : private NonCopyable {
|
||||||
|
protected:
|
||||||
|
PacketVisitor() {}
|
||||||
|
virtual ~PacketVisitor() {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* \brief Calls the right PacketVisitor::Visit method corresponding to the real type of the packet
|
||||||
|
* \param packet the Packet to visit
|
||||||
|
*/
|
||||||
|
void Check(const Packet& packet);
|
||||||
|
|
||||||
|
DeclareAllPacket()
|
||||||
|
};
|
||||||
|
|
||||||
|
#undef DeclarePacket
|
||||||
|
|
||||||
|
} // namespace protocol
|
||||||
|
} // namespace blitz
|
||||||
107
include/blitz/protocol/Packets.h
Normal file
107
include/blitz/protocol/Packets.h
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file Packets.h
|
||||||
|
* \brief File containing the definitions of the packets
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <blitz/common/NonCopyable.h>
|
||||||
|
#include <blitz/protocol/PacketData.h>
|
||||||
|
#include <blitz/protocol/PacketDeclare.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace protocol {
|
||||||
|
|
||||||
|
class PacketVisitor;
|
||||||
|
|
||||||
|
/** A Packet id is 8 bits wide */
|
||||||
|
using PacketID = std::uint8_t;
|
||||||
|
|
||||||
|
#define DeclarePacket(PacketName, ...) /** PacketName */ PacketName,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \enum PacketType
|
||||||
|
* \brief Map a Packet to an id
|
||||||
|
*/
|
||||||
|
enum class PacketType : PacketID {
|
||||||
|
|
||||||
|
DeclareAllPacket()
|
||||||
|
|
||||||
|
/** The number of packets */
|
||||||
|
PACKET_COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#undef DeclarePacket
|
||||||
|
|
||||||
|
|
||||||
|
class Packet : private NonCopyable {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* \return The real type of the packet
|
||||||
|
*/
|
||||||
|
virtual PacketType GetType() const = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/** Use a PacketVisitor to make double-dispatch possible */
|
||||||
|
virtual void Accept(PacketVisitor& a_Visitor) const = 0;
|
||||||
|
|
||||||
|
friend class PacketVisitor;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace packets {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \class ConcretePacket
|
||||||
|
* \brief A Packet associated with an id and holding data
|
||||||
|
* \tparam PT The packet type
|
||||||
|
* \tparam Data The structure holding the data of the packet (in blitz::protocol::data namespace)
|
||||||
|
*/
|
||||||
|
template <PacketType PT, typename Data>
|
||||||
|
class ConcretePacket : public Packet {
|
||||||
|
public:
|
||||||
|
/** The type of the struct holding the data */
|
||||||
|
using PacketDataType = Data;
|
||||||
|
|
||||||
|
/** The structure holding the actual data */
|
||||||
|
PacketDataType m_Data;
|
||||||
|
|
||||||
|
/** Construct the packet with data of type PacketDataType */
|
||||||
|
ConcretePacket(const PacketDataType& a_Data = {});
|
||||||
|
|
||||||
|
constexpr PacketType GetType() const override {
|
||||||
|
return PT;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Accept(PacketVisitor& a_Visitor) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// define BLITZ_INSTANCIATE_PACKETS
|
||||||
|
// before including this file
|
||||||
|
// if you want to instantiate templates
|
||||||
|
#ifdef BLITZ_INSTANCIATE_PACKETS
|
||||||
|
#define DeclarePacket(PacketName, ...) \
|
||||||
|
using PacketName = ConcretePacket<PacketType::PacketName, data::PacketName>; \
|
||||||
|
template class ConcretePacket<PacketType::PacketName, data::PacketName>;
|
||||||
|
#else
|
||||||
|
#define DeclarePacket(PacketName, ...) /** Defines the PacketName packet */ \
|
||||||
|
using PacketName = ConcretePacket<PacketType::PacketName, data::PacketName>;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DeclareAllPacket()
|
||||||
|
|
||||||
|
#undef DeclarePacket
|
||||||
|
|
||||||
|
} // namespace packets
|
||||||
|
|
||||||
|
} // namespace protocol
|
||||||
|
} // namespace blitz
|
||||||
18
include/blitz/systems/RemovePlayersSystem.h
Normal file
18
include/blitz/systems/RemovePlayersSystem.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Nazara/Core/Time.hpp>
|
||||||
|
#include <blitz/common/Types.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
|
||||||
|
class RemovePlayersSystem {
|
||||||
|
public:
|
||||||
|
RemovePlayersSystem(entt::registry&, EnttWorld& a_World);
|
||||||
|
|
||||||
|
void Update(Nz::Time elapsedTime);
|
||||||
|
|
||||||
|
private:
|
||||||
|
EnttWorld& m_World;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace blitz
|
||||||
70
include/blitz/utils/Test.h
Normal file
70
include/blitz/utils/Test.h
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file Test.h
|
||||||
|
* \brief File containing unit testing utilities
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <blitz/common/Log.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \def BLITZ_TEST_SUCCESSFUL
|
||||||
|
* \brief Used in tests to indicate that a test was successful
|
||||||
|
*/
|
||||||
|
#define BLITZ_TEST_SUCCESSFUL 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \def BLITZ_TEST_FAILED
|
||||||
|
* \brief Used in tests to indicate that a test failed
|
||||||
|
*/
|
||||||
|
#define BLITZ_TEST_FAILED 1
|
||||||
|
|
||||||
|
#ifndef __FUNCTION_NAME__
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define __FUNCTION_NAME__ __FUNCTION__
|
||||||
|
#else
|
||||||
|
#define __FUNCTION_NAME__ __PRETTY_FUNCTION__
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \def blitz_test_assert
|
||||||
|
* \param ... The expression to evaluate
|
||||||
|
* \brief Evaluates the expression and exits the program if not valid.
|
||||||
|
* \note It works like a basic assert() but also in release mode
|
||||||
|
*/
|
||||||
|
#define blitz_test_assert(...) \
|
||||||
|
if (!static_cast<bool>(__VA_ARGS__)) { \
|
||||||
|
blitz::test::LogAssert(#__VA_ARGS__, __FILE__, __LINE__, __FUNCTION_NAME__); \
|
||||||
|
std::exit(BLITZ_TEST_FAILED); \
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \def blitz_debug_assert
|
||||||
|
* \param ... The expression to execute
|
||||||
|
* \brief Assertion without checks in release mode
|
||||||
|
* \note The expression is always executed. However, in release, no checks are made !
|
||||||
|
*/
|
||||||
|
#ifdef NDEBUG
|
||||||
|
#define blitz_debug_assert(...) __VA_ARGS__
|
||||||
|
#else
|
||||||
|
#define blitz_debug_assert blitz_test_assert
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Prints an error message associated with a failed assertion
|
||||||
|
* \param expression The expression that was tested
|
||||||
|
* \param file The file in which the assertion failed
|
||||||
|
* \param line The line in the file in which the assertion failed
|
||||||
|
* \param function The function in which the assertion failed
|
||||||
|
*/
|
||||||
|
void LogAssert(const char* expression, const char* file, int line, const char* function);
|
||||||
|
|
||||||
|
} // namespace test
|
||||||
|
} // namespace blitz
|
||||||
34
include/client/Client.h
Normal file
34
include/client/Client.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Nazara/Core/EnttWorld.hpp>
|
||||||
|
#include <blitz/network/EnetClient.h>
|
||||||
|
#include <blitz/protocol/PacketHandler.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
class Client : private NonCopyable {
|
||||||
|
public:
|
||||||
|
Client(Nz::EnttWorld& a_World);
|
||||||
|
~Client();
|
||||||
|
|
||||||
|
void Connect(const Nz::IpAddress& a_Ip);
|
||||||
|
void Disconnect();
|
||||||
|
|
||||||
|
bool IsConnected();
|
||||||
|
|
||||||
|
NazaraSignal(OnClientReady);
|
||||||
|
NazaraSignal(OnClientDisconnect);
|
||||||
|
|
||||||
|
private:
|
||||||
|
EnttWorld m_World;
|
||||||
|
std::unique_ptr<network::EnetClient> m_NetworkClient;
|
||||||
|
std::vector<std::unique_ptr<protocol::PacketHandler>> m_Handlers;
|
||||||
|
|
||||||
|
void BindHandlers();
|
||||||
|
void UnbindHandlers();
|
||||||
|
void Login();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace blitz
|
||||||
36
include/client/ClientApp.h
Normal file
36
include/client/ClientApp.h
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Nazara/Core/ApplicationComponent.hpp>
|
||||||
|
|
||||||
|
namespace Nz {
|
||||||
|
|
||||||
|
class Window;
|
||||||
|
class Canvas;
|
||||||
|
class StateMachine;
|
||||||
|
|
||||||
|
} // namespace Nz
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
class StateData;
|
||||||
|
|
||||||
|
class ClientApp : public Nz::ApplicationComponent {
|
||||||
|
public:
|
||||||
|
ClientApp(Nz::ApplicationBase& app);
|
||||||
|
~ClientApp();
|
||||||
|
|
||||||
|
void Update(Nz::Time elapsedTime) override;
|
||||||
|
|
||||||
|
Nz::Window* GetWindow() {
|
||||||
|
return m_Window;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Nz::Window* m_Window;
|
||||||
|
std::unique_ptr<Nz::StateMachine> m_StateMachine;
|
||||||
|
std::shared_ptr<StateData> m_StateData;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace blitz
|
||||||
33
include/client/ImGuiAppComponent.h
Normal file
33
include/client/ImGuiAppComponent.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Nazara/Core/ApplicationComponent.hpp>
|
||||||
|
|
||||||
|
namespace Nz {
|
||||||
|
|
||||||
|
class Window;
|
||||||
|
class Texture;
|
||||||
|
|
||||||
|
} // namespace Nz
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
class StateData;
|
||||||
|
|
||||||
|
class ImGuiAppComponent : public Nz::ApplicationComponent {
|
||||||
|
public:
|
||||||
|
ImGuiAppComponent(Nz::ApplicationBase& app, Nz::Window& a_Window);
|
||||||
|
~ImGuiAppComponent();
|
||||||
|
|
||||||
|
void Update(Nz::Time elapsedTime) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Nz::Window& m_Window;
|
||||||
|
std::shared_ptr<Nz::Texture> m_FontTexture;
|
||||||
|
|
||||||
|
void SetStyle();
|
||||||
|
void UpdateFontTexture();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace blitz
|
||||||
13
include/client/components/LocalPlayer.h
Normal file
13
include/client/components/LocalPlayer.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <blitz/common/Types.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
struct LocalPlayerComponent {
|
||||||
|
EntityID m_LocalPlayerId;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace blitz
|
||||||
18
include/client/handlers/KeepAliveHandler.h
Normal file
18
include/client/handlers/KeepAliveHandler.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#include <blitz/protocol/PacketHandler.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
class KeepAliveHandler : public protocol::PacketHandler {
|
||||||
|
public:
|
||||||
|
KeepAliveHandler(network::EnetConnection& a_Connection, EnttWorld& a_World);
|
||||||
|
~KeepAliveHandler();
|
||||||
|
|
||||||
|
NazaraSlot(network::EnetConnection, OnKeepAlive, m_Slot);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Handle(const protocol::data::KeepAlive&);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace blitz
|
||||||
18
include/client/handlers/LoggingSuccessHandler.h
Normal file
18
include/client/handlers/LoggingSuccessHandler.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#include <blitz/protocol/PacketHandler.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
class LoggingSuccessHandler : public protocol::PacketHandler {
|
||||||
|
public:
|
||||||
|
LoggingSuccessHandler(network::EnetConnection& a_Connection, EnttWorld& a_World);
|
||||||
|
~LoggingSuccessHandler();
|
||||||
|
|
||||||
|
NazaraSlot(network::EnetConnection, OnLoggingSuccess, m_Slot);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Handle(const protocol::data::LoggingSuccess&);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace blitz
|
||||||
20
include/client/handlers/PlayerJoinHandler.h
Normal file
20
include/client/handlers/PlayerJoinHandler.h
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#include <blitz/protocol/PacketHandler.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
class PlayerJoinHandler : public protocol::PacketHandler {
|
||||||
|
public:
|
||||||
|
PlayerJoinHandler(network::EnetConnection& a_Connection, EnttWorld& a_World);
|
||||||
|
~PlayerJoinHandler();
|
||||||
|
|
||||||
|
NazaraSlot(network::EnetConnection, OnPlayerJoin, m_Slot);
|
||||||
|
|
||||||
|
NazaraSignal(OnLocalPlayerReady);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Handle(const protocol::data::PlayerJoin&);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace blitz
|
||||||
18
include/client/handlers/PlayerLeaveHandler.h
Normal file
18
include/client/handlers/PlayerLeaveHandler.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#include <blitz/protocol/PacketHandler.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
class PlayerLeaveHandler : public protocol::PacketHandler {
|
||||||
|
public:
|
||||||
|
PlayerLeaveHandler(network::EnetConnection& a_Connection, EnttWorld& a_World);
|
||||||
|
~PlayerLeaveHandler();
|
||||||
|
|
||||||
|
NazaraSlot(network::EnetConnection, OnPlayerLeave, m_Slot);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Handle(const protocol::data::PlayerLeave&);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace blitz
|
||||||
18
include/client/handlers/PlayerListHandler.h
Normal file
18
include/client/handlers/PlayerListHandler.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#include <blitz/protocol/PacketHandler.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
class PlayerListHandler : public protocol::PacketHandler {
|
||||||
|
public:
|
||||||
|
PlayerListHandler(network::EnetConnection& a_Connection, EnttWorld& a_World);
|
||||||
|
~PlayerListHandler();
|
||||||
|
|
||||||
|
NazaraSlot(network::EnetConnection, OnPlayerList, m_Slot);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Handle(const protocol::data::PlayerList&);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace blitz
|
||||||
75
include/client/states/AbstractState.h
Normal file
75
include/client/states/AbstractState.h
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Nazara/Core/Components/DisabledComponent.hpp>
|
||||||
|
#include <Nazara/Core/State.hpp>
|
||||||
|
#include <Nazara/Graphics/RenderTarget.hpp>
|
||||||
|
#include <Nazara/Widgets/BaseWidget.hpp>
|
||||||
|
#include <client/states/StateData.h>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
class AbstractState : public Nz::State, public std::enable_shared_from_this<AbstractState> {
|
||||||
|
public:
|
||||||
|
AbstractState(std::shared_ptr<StateData> stateData);
|
||||||
|
~AbstractState();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
template <typename T, typename... Args>
|
||||||
|
void ConnectSignal(T& signal, Args&&... args) {
|
||||||
|
m_CleanupFunctions.emplace_back(
|
||||||
|
[connection = signal.Connect(std::forward<Args>(args)...)]() mutable { connection.Disconnect(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename... Args>
|
||||||
|
T* CreateWidget(Args&&... args) {
|
||||||
|
T* widget = m_StateData->m_Canvas->Add<T>(std::forward<Args>(args)...);
|
||||||
|
|
||||||
|
auto& entry = m_Widgets.emplace_back();
|
||||||
|
entry.m_Widget = widget;
|
||||||
|
|
||||||
|
if (!m_IsVisible)
|
||||||
|
entry.m_Widget->Hide();
|
||||||
|
|
||||||
|
return widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DestroyWidget(Nz::BaseWidget* widget);
|
||||||
|
|
||||||
|
StateData& GetStateData() {
|
||||||
|
return *m_StateData;
|
||||||
|
}
|
||||||
|
|
||||||
|
const StateData& GetStateData() const {
|
||||||
|
return *m_StateData;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::shared_ptr<StateData>& GetStateDataPtr() {
|
||||||
|
return m_StateData;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Enter(Nz::StateMachine& fsm) override;
|
||||||
|
void Leave(Nz::StateMachine& fsm) override;
|
||||||
|
bool Update(Nz::StateMachine& fsm, Nz::Time elapsedTime) override;
|
||||||
|
|
||||||
|
virtual void LayoutWidgets() {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
NazaraSlot(Nz::RenderTarget, OnRenderTargetSizeChange, m_OnTargetChangeSizeSlot);
|
||||||
|
|
||||||
|
struct WidgetEntry {
|
||||||
|
Nz::BaseWidget* m_Widget;
|
||||||
|
bool m_WasVisible = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<StateData> m_StateData;
|
||||||
|
std::vector<std::function<void()>> m_CleanupFunctions;
|
||||||
|
std::vector<WidgetEntry> m_Widgets;
|
||||||
|
bool m_IsVisible;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace blitz
|
||||||
62
include/client/states/ConnectingState.h
Normal file
62
include/client/states/ConnectingState.h
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Nazara/Network/IpAddress.hpp>
|
||||||
|
#include <Nazara/Widgets/ButtonWidget.hpp>
|
||||||
|
#include <Nazara/Widgets/LabelWidget.hpp>
|
||||||
|
#include <client/Client.h>
|
||||||
|
#include <client/states/AbstractState.h>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
|
||||||
|
namespace server {
|
||||||
|
class Server;
|
||||||
|
} // namespace server
|
||||||
|
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
class ConnectingState : public AbstractState {
|
||||||
|
public:
|
||||||
|
ConnectingState(std::shared_ptr<StateData> a_StateData, std::shared_ptr<AbstractState> a_PreviousState,
|
||||||
|
const std::string& a_Address, std::uint16_t a_Port, bool a_IntegratedServer = false);
|
||||||
|
~ConnectingState();
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct ResolvingData {
|
||||||
|
bool m_HasResult = false;
|
||||||
|
Nz::Result<std::vector<Nz::IpAddress>, std::string /*error*/> m_Result = Nz::Err("");
|
||||||
|
std::string m_ServerName;
|
||||||
|
std::uint16_t m_Port;
|
||||||
|
};
|
||||||
|
|
||||||
|
ResolvingData m_ResolvingData;
|
||||||
|
bool m_IsConnected = false;
|
||||||
|
std::queue<Nz::IpAddress> m_Addresses;
|
||||||
|
Nz::MillisecondClock m_TimeoutClock;
|
||||||
|
|
||||||
|
Nz::ButtonWidget* m_BackButton;
|
||||||
|
Nz::LabelWidget* m_StatusText;
|
||||||
|
std::shared_ptr<AbstractState> m_NextState;
|
||||||
|
std::shared_ptr<AbstractState> m_PreviousState;
|
||||||
|
|
||||||
|
std::unique_ptr<Client> m_Client;
|
||||||
|
std::unique_ptr<server::Server> m_Server;
|
||||||
|
|
||||||
|
void LayoutWidgets() override;
|
||||||
|
bool Update(Nz::StateMachine& fsm, Nz::Time elapsedTime) override;
|
||||||
|
|
||||||
|
void TryResolve();
|
||||||
|
void TryConnect(const Nz::IpAddress& a_ServerAddress);
|
||||||
|
void TryNextAddress();
|
||||||
|
|
||||||
|
void OnBackPressed();
|
||||||
|
|
||||||
|
void OnConnect();
|
||||||
|
void OnConnectFailed();
|
||||||
|
|
||||||
|
NazaraSlot(Client, OnClientReady, m_ClientReady);
|
||||||
|
NazaraSlot(Client, OnClientDisconnect, m_ClientDisconnect);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace blitz
|
||||||
30
include/client/states/CreateServerState.h
Normal file
30
include/client/states/CreateServerState.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Nazara/Widgets/TextAreaWidget.hpp>
|
||||||
|
#include <Nazara/Widgets/ButtonWidget.hpp>
|
||||||
|
#include <client/states/AbstractState.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
class CreateServerState : public AbstractState {
|
||||||
|
public:
|
||||||
|
CreateServerState(std::shared_ptr<StateData> a_StateData, std::shared_ptr<AbstractState> a_PreviousState);
|
||||||
|
~CreateServerState();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Nz::ButtonWidget* m_CreateServerButton;
|
||||||
|
Nz::TextAreaWidget* m_InputPort;
|
||||||
|
Nz::ButtonWidget* m_BackButton;
|
||||||
|
std::shared_ptr<AbstractState> m_NextState;
|
||||||
|
std::shared_ptr<AbstractState> m_PreviousState;
|
||||||
|
|
||||||
|
void LayoutWidgets() override;
|
||||||
|
bool Update(Nz::StateMachine& fsm, Nz::Time elapsedTime) override;
|
||||||
|
|
||||||
|
void OnCreateServerPressed();
|
||||||
|
void OnBackPressed();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace blitz
|
||||||
27
include/client/states/GameState.h
Normal file
27
include/client/states/GameState.h
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <client/states/AbstractState.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
|
||||||
|
namespace server {
|
||||||
|
class Server;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
class Client;
|
||||||
|
|
||||||
|
class GameState : public AbstractState {
|
||||||
|
public:
|
||||||
|
GameState(std::shared_ptr<StateData> a_StateData, std::unique_ptr<Client>&& a_Client, std::unique_ptr<server::Server>&& a_Server);
|
||||||
|
~GameState();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<Client> m_Client;
|
||||||
|
std::unique_ptr<server::Server> m_Server;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace blitz
|
||||||
17
include/client/states/ImGuiDrawer.h
Normal file
17
include/client/states/ImGuiDrawer.h
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <NazaraImgui/ImguiHandler.hpp>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
class ImGuiDrawer : private Nz::ImguiHandler {
|
||||||
|
protected:
|
||||||
|
ImGuiDrawer();
|
||||||
|
~ImGuiDrawer();
|
||||||
|
|
||||||
|
virtual void OnRenderImgui() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace blitz
|
||||||
30
include/client/states/JoinServerState.h
Normal file
30
include/client/states/JoinServerState.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Nazara/Widgets/ButtonWidget.hpp>
|
||||||
|
#include <Nazara/Widgets/TextAreaWidget.hpp>
|
||||||
|
#include <client/states/AbstractState.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
class JoinServerState : public AbstractState {
|
||||||
|
public:
|
||||||
|
JoinServerState(std::shared_ptr<StateData> a_StateData, std::shared_ptr<AbstractState> a_PreviousState);
|
||||||
|
~JoinServerState();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Nz::TextAreaWidget* m_InputAddress;
|
||||||
|
Nz::ButtonWidget* m_JoinServerButton;
|
||||||
|
Nz::ButtonWidget* m_BackButton;
|
||||||
|
std::shared_ptr<AbstractState> m_NextState;
|
||||||
|
std::shared_ptr<AbstractState> m_PreviousState;
|
||||||
|
|
||||||
|
void LayoutWidgets() override;
|
||||||
|
bool Update(Nz::StateMachine& fsm, Nz::Time elapsedTime) override;
|
||||||
|
|
||||||
|
void OnJoinServerPressed();
|
||||||
|
void OnBackPressed();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace blitz
|
||||||
34
include/client/states/MainMenuState.h
Normal file
34
include/client/states/MainMenuState.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Nazara/Widgets/ButtonWidget.hpp>
|
||||||
|
#include <client/states/AbstractState.h>
|
||||||
|
#include <client/states/ImGuiDrawer.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
class MainMenuState : public AbstractState, private ImGuiDrawer {
|
||||||
|
public:
|
||||||
|
MainMenuState(std::shared_ptr<StateData>);
|
||||||
|
~MainMenuState();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Nz::ButtonWidget* m_JoinServerButton;
|
||||||
|
Nz::ButtonWidget* m_CreateServerButton;
|
||||||
|
Nz::ButtonWidget* m_OptionButton;
|
||||||
|
Nz::ButtonWidget* m_QuitButton;
|
||||||
|
std::shared_ptr<AbstractState> m_NextState;
|
||||||
|
|
||||||
|
void LayoutWidgets() override;
|
||||||
|
bool Update(Nz::StateMachine& fsm, Nz::Time elapsedTime) override;
|
||||||
|
|
||||||
|
void OnRenderImgui() override;
|
||||||
|
|
||||||
|
void OnJoinServerPressed();
|
||||||
|
void OnCreateServerPressed();
|
||||||
|
void OnOptionPressed();
|
||||||
|
void OnQuitPressed();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace blitz
|
||||||
28
include/client/states/OptionState.h
Normal file
28
include/client/states/OptionState.h
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Nazara/Widgets/ButtonWidget.hpp>
|
||||||
|
#include <client/states/AbstractState.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
class OptionState : public AbstractState {
|
||||||
|
public:
|
||||||
|
OptionState(std::shared_ptr<StateData> a_StateData, std::shared_ptr<AbstractState> a_PreviousState);
|
||||||
|
~OptionState();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Nz::ButtonWidget* m_OptionButton;
|
||||||
|
Nz::ButtonWidget* m_BackButton;
|
||||||
|
std::shared_ptr<AbstractState> m_NextState;
|
||||||
|
std::shared_ptr<AbstractState> m_PreviousState;
|
||||||
|
|
||||||
|
void LayoutWidgets() override;
|
||||||
|
bool Update(Nz::StateMachine& fsm, Nz::Time elapsedTime) override;
|
||||||
|
|
||||||
|
void OnOptionPressed();
|
||||||
|
void OnBackPressed();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace blitz
|
||||||
34
include/client/states/StateData.h
Normal file
34
include/client/states/StateData.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Nazara/Core/EnttWorld.hpp>
|
||||||
|
#include <Nazara/Platform/Window.hpp>
|
||||||
|
#include <Nazara/Renderer/WindowSwapchain.hpp>
|
||||||
|
#include <Nazara/Widgets/Canvas.hpp>
|
||||||
|
#include <entt/fwd.hpp>
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
namespace Nz {
|
||||||
|
|
||||||
|
class ApplicationBase;
|
||||||
|
class RenderTarget;
|
||||||
|
|
||||||
|
} // namespace Nz
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
class ClientApp;
|
||||||
|
|
||||||
|
struct StateData {
|
||||||
|
std::optional<Nz::Canvas> m_Canvas;
|
||||||
|
std::shared_ptr<Nz::RenderTarget> m_RenderTarget;
|
||||||
|
ClientApp* m_AppComponent;
|
||||||
|
Nz::ApplicationBase* m_App;
|
||||||
|
Nz::EnttWorld* m_World;
|
||||||
|
Nz::Window* m_Window;
|
||||||
|
Nz::WindowSwapchain* m_Swapchain;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace blitz
|
||||||
45
include/server/Server.h
Normal file
45
include/server/Server.h
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <blitz/network/EnetServer.h>
|
||||||
|
#include <blitz/protocol/PacketHandler.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
class Server {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* \brief Construct a server
|
||||||
|
* \pre Two instances of Server should not share the same world
|
||||||
|
*/
|
||||||
|
Server(std::uint16_t a_Port, Nz::EnttWorld& a_World);
|
||||||
|
~Server();
|
||||||
|
|
||||||
|
network::EnetConnection* GetConnection(std::uint16_t a_PeerId);
|
||||||
|
|
||||||
|
bool IsClosed() const;
|
||||||
|
|
||||||
|
void CloseConnection(std::uint16_t a_PeerId);
|
||||||
|
|
||||||
|
void CloseServer();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void HandleConnect(network::EnetConnection&);
|
||||||
|
void HandleDisconnect(network::EnetConnection&);
|
||||||
|
|
||||||
|
struct Session {
|
||||||
|
network::EnetConnection* m_Connection;
|
||||||
|
std::vector<std::unique_ptr<protocol::PacketHandler>> m_Handlers;
|
||||||
|
};
|
||||||
|
|
||||||
|
EnttWorld m_World;
|
||||||
|
std::map<std::uint16_t, Session> m_Sessions;
|
||||||
|
network::EnetServer m_NetworkServer;
|
||||||
|
|
||||||
|
void RegisterHandlers(Session& session);
|
||||||
|
void RegisterSystems();
|
||||||
|
void CreateEntity(network::EnetConnection&);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace server
|
||||||
|
} // namespace blitz
|
||||||
13
include/server/components/Disconnect.h
Normal file
13
include/server/components/Disconnect.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
enum Reason { Timeout };
|
||||||
|
|
||||||
|
struct DisconnectComponent {
|
||||||
|
Reason m_Reason;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace server
|
||||||
|
} // namespace blitz
|
||||||
13
include/server/components/EnetConnection.h
Normal file
13
include/server/components/EnetConnection.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <blitz/network/EnetConnection.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
struct EnetConnectionComponent {
|
||||||
|
network::EnetConnection* m_Connection;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace server
|
||||||
|
} // namespace blitz
|
||||||
17
include/server/components/KeepAliveSession.h
Normal file
17
include/server/components/KeepAliveSession.h
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Nazara/Core/Time.hpp>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
struct KeepAliveSessionComponent {
|
||||||
|
std::uint16_t m_PeerId;
|
||||||
|
std::uint64_t m_LastKeepAliveId;
|
||||||
|
Nz::Time m_LastTime;
|
||||||
|
bool m_RecievedResponse = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace server
|
||||||
|
} // namespace blitz
|
||||||
13
include/server/components/ServerIdCounter.h
Normal file
13
include/server/components/ServerIdCounter.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <blitz/common/Types.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
struct ServerIdCounterComponent {
|
||||||
|
EntityID m_NextEntityId;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace server
|
||||||
|
} // namespace blitz
|
||||||
20
include/server/handlers/KeepAliveHandler.h
Normal file
20
include/server/handlers/KeepAliveHandler.h
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <blitz/protocol/PacketHandler.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
class KeepAliveHandler : public protocol::PacketHandler {
|
||||||
|
public:
|
||||||
|
KeepAliveHandler(network::EnetConnection& a_Connection, EnttWorld& a_World);
|
||||||
|
~KeepAliveHandler();
|
||||||
|
|
||||||
|
NazaraSlot(network::EnetConnection, OnKeepAlive, m_Slot);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Handle(std::uint16_t, const protocol::data::KeepAlive&);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace server
|
||||||
|
} // namespace blitz
|
||||||
20
include/server/handlers/PlayerLoginHandler.h
Normal file
20
include/server/handlers/PlayerLoginHandler.h
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <blitz/protocol/PacketHandler.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
class PlayerLoginHandler : public protocol::PacketHandler {
|
||||||
|
public:
|
||||||
|
PlayerLoginHandler(network::EnetConnection& a_Connection, EnttWorld& a_World);
|
||||||
|
~PlayerLoginHandler();
|
||||||
|
|
||||||
|
NazaraSlot(network::EnetConnection, OnPlayerLogin, m_Slot);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Handle(std::uint16_t, const protocol::data::PlayerLogin&);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace server
|
||||||
|
} // namespace blitz
|
||||||
23
include/server/systems/DisconnectSystem.h
Normal file
23
include/server/systems/DisconnectSystem.h
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Nazara/Core/Time.hpp>
|
||||||
|
#include <blitz/common/Types.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
class Server;
|
||||||
|
|
||||||
|
class DisconnectSystem {
|
||||||
|
public:
|
||||||
|
DisconnectSystem(entt::registry&, EnttWorld& a_World, Server& a_Server);
|
||||||
|
|
||||||
|
void Update(Nz::Time elapsedTime);
|
||||||
|
|
||||||
|
private:
|
||||||
|
EnttWorld& m_World;
|
||||||
|
Server& m_Server;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace server
|
||||||
|
} // namespace blitz
|
||||||
21
include/server/systems/KeepAliveSystem.h
Normal file
21
include/server/systems/KeepAliveSystem.h
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Nazara/Core/Time.hpp>
|
||||||
|
#include <entt/entity/registry.hpp>
|
||||||
|
#include <blitz/common/Types.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
class KeepAliveSystem {
|
||||||
|
public:
|
||||||
|
KeepAliveSystem(entt::registry&, EnttWorld& a_World);
|
||||||
|
|
||||||
|
void Update(Nz::Time elapsedTime);
|
||||||
|
|
||||||
|
private:
|
||||||
|
EnttWorld& m_World;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace server
|
||||||
|
} // namespace blitz
|
||||||
20
src/ClientMain.cpp
Normal file
20
src/ClientMain.cpp
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#include <Nazara/Core/Application.hpp>
|
||||||
|
#include <Nazara/Core/EntitySystemAppComponent.hpp>
|
||||||
|
#include <Nazara/Graphics/Graphics.hpp>
|
||||||
|
#include <Nazara/Physics3D/Physics3D.hpp>
|
||||||
|
#include <Nazara/Platform/WindowingAppComponent.hpp>
|
||||||
|
#include <Nazara/Widgets/Widgets.hpp>
|
||||||
|
#include <NazaraImgui/NazaraImgui.hpp>
|
||||||
|
#include <client/ClientApp.h>
|
||||||
|
#include <client/ImGuiAppComponent.h>
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
Nz::Application<Nz::Graphics, Nz::Physics3D, Nz::Widgets, Nz::TextRenderer, Nz::Imgui> app(argc, argv);
|
||||||
|
|
||||||
|
app.AddComponent<Nz::EntitySystemAppComponent>();
|
||||||
|
app.AddComponent<Nz::WindowingAppComponent>();
|
||||||
|
auto& client = app.AddComponent<blitz::client::ClientApp>();
|
||||||
|
app.AddComponent<blitz::client::ImGuiAppComponent>(*client.GetWindow());
|
||||||
|
|
||||||
|
return app.Run();
|
||||||
|
}
|
||||||
56
src/ServerMain.cpp
Normal file
56
src/ServerMain.cpp
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
#include <server/Server.h>
|
||||||
|
|
||||||
|
#include <Nazara/Core/Application.hpp>
|
||||||
|
#include <Nazara/Core/EntitySystemAppComponent.hpp>
|
||||||
|
#include <Nazara/Network/Network.hpp>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
void ConsoleLoop(Nz::ApplicationBase& app, blitz::server::Server& server) {
|
||||||
|
std::string line;
|
||||||
|
while (true) {
|
||||||
|
getline(std::cin, line);
|
||||||
|
if (line == "stop") {
|
||||||
|
std::cout << "Exiting ...\n";
|
||||||
|
server.CloseServer();
|
||||||
|
app.Quit();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::uint16_t DefaultPort = 25565;
|
||||||
|
|
||||||
|
int main(int argc, char* args[]) {
|
||||||
|
Nz::Application<Nz::Network> app(argc, args);
|
||||||
|
|
||||||
|
const auto& params = app.GetCommandLineParameters();
|
||||||
|
|
||||||
|
std::string_view portString;
|
||||||
|
std::uint16_t port = DefaultPort;
|
||||||
|
|
||||||
|
if (params.GetParameter("port", &portString)) {
|
||||||
|
std::cout << "Selected port : " << portString << "\n";
|
||||||
|
bool ok;
|
||||||
|
port = Nz::StringToNumber(portString, 10, &ok);
|
||||||
|
if (!ok) {
|
||||||
|
std::cerr << "Failed to parse port !\n";
|
||||||
|
port = DefaultPort;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& ecs = app.AddComponent<Nz::EntitySystemAppComponent>();
|
||||||
|
|
||||||
|
auto& serverWorld = ecs.AddWorld<Nz::EnttWorld>();
|
||||||
|
|
||||||
|
blitz::server::Server server(port, serverWorld);
|
||||||
|
|
||||||
|
std::cout << "Server running on port " << port << " ...\n";
|
||||||
|
|
||||||
|
std::jthread consoleThread([&app, &server]() { ConsoleLoop(app, server); });
|
||||||
|
|
||||||
|
// tick 20 times per seconds
|
||||||
|
app.AddUpdaterFunc([]() { std::this_thread::sleep_for(std::chrono::milliseconds(50)); });
|
||||||
|
|
||||||
|
return app.Run();
|
||||||
|
}
|
||||||
35
src/blitz/common/Log.cpp
Normal file
35
src/blitz/common/Log.cpp
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#include <blitz/common/Log.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef BLITZ_ANDROID_LOGGING
|
||||||
|
#include <android/log.h>
|
||||||
|
#else
|
||||||
|
#include <iostream>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
|
||||||
|
void Log(const std::string& msg) {
|
||||||
|
#ifdef BLITZ_ANDROID_LOGGING
|
||||||
|
__android_log_print(ANDROID_LOG_INFO, "TRACKERS", "%s", msg.c_str());
|
||||||
|
#else
|
||||||
|
std::cout << msg << "\n";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogD(const std::string& msg) {
|
||||||
|
#if !defined(NDEBUG)
|
||||||
|
Log(msg);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogE(const std::string& err) {
|
||||||
|
#ifdef BLITZ_ANDROID_LOGGING
|
||||||
|
__android_log_print(ANDROID_LOG_ERROR, "TRACKERS", "%s", err.c_str());
|
||||||
|
#else
|
||||||
|
std::cerr << err << "\n";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace blitz
|
||||||
64
src/blitz/network/EnetClient.cpp
Normal file
64
src/blitz/network/EnetClient.cpp
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
#include <blitz/network/EnetClient.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace network {
|
||||||
|
|
||||||
|
EnetClient::EnetClient(const Nz::IpAddress& address) : m_Running(true) {
|
||||||
|
m_Host.Create(address.GetProtocol() == Nz::NetProtocol::IPv4 ? Nz::IpAddress::LoopbackIpV4 : Nz::IpAddress::LoopbackIpV6, 1);
|
||||||
|
m_Peer = m_Host.Connect(address);
|
||||||
|
m_Thread = std::jthread(&EnetClient::WorkerThread, this);
|
||||||
|
m_Connection.SetPeer(m_Peer);
|
||||||
|
}
|
||||||
|
|
||||||
|
EnetClient::~EnetClient() {
|
||||||
|
if (m_Peer->IsConnected())
|
||||||
|
Disconnect();
|
||||||
|
m_Running = false;
|
||||||
|
m_Thread.request_stop();
|
||||||
|
m_Host.Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnetClient::Disconnect() {
|
||||||
|
m_Peer->DisconnectNow(0);
|
||||||
|
m_Connection.SetPeer(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnetClient::WorkerThread() {
|
||||||
|
while (m_Running) {
|
||||||
|
Update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnetClient::Update() {
|
||||||
|
Nz::ENetEvent event;
|
||||||
|
int service = m_Host.Service(&event, 5);
|
||||||
|
if (service > 0) {
|
||||||
|
do {
|
||||||
|
switch (event.type) {
|
||||||
|
case Nz::ENetEventType::Disconnect:
|
||||||
|
OnDisconnect();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Nz::ENetEventType::DisconnectTimeout:
|
||||||
|
OnDisconnectTimeout();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Nz::ENetEventType::OutgoingConnect:
|
||||||
|
OnConnect();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Nz::ENetEventType::Receive:
|
||||||
|
m_Connection.Recieve(event.packet.m_packet->data);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Nz::ENetEventType::None:
|
||||||
|
case Nz::ENetEventType::IncomingConnect:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (m_Host.CheckEvents(&event));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace network
|
||||||
|
} // namespace blitz
|
||||||
66
src/blitz/network/EnetConnection.cpp
Normal file
66
src/blitz/network/EnetConnection.cpp
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#include <blitz/network/EnetConnection.h>
|
||||||
|
|
||||||
|
#include <Nazara/Network/ENetPeer.hpp>
|
||||||
|
#include <blitz/protocol/PacketSerializer.h>
|
||||||
|
#include <blitz/protocol/PacketVisitor.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace network {
|
||||||
|
|
||||||
|
namespace packets = blitz::protocol::packets;
|
||||||
|
|
||||||
|
|
||||||
|
#define DeclarePacket(PacketName, ...) \
|
||||||
|
void Visit(const protocol::packets::PacketName& a_Packet) { m_Connection.On##PacketName(a_Packet.m_Data); }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class PacketDispatcher : public protocol::PacketVisitor {
|
||||||
|
public:
|
||||||
|
PacketDispatcher(EnetConnection& a_Connection) : m_Connection(a_Connection) {}
|
||||||
|
|
||||||
|
DeclareAllPacket()
|
||||||
|
|
||||||
|
private : EnetConnection& m_Connection;
|
||||||
|
};
|
||||||
|
|
||||||
|
#undef DeclarePacket
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
EnetConnection::EnetConnection(Nz::ENetPeer* a_Peer) : m_Peer(a_Peer) {}
|
||||||
|
|
||||||
|
void EnetConnection::SetPeer(Nz::ENetPeer* a_Peer) {
|
||||||
|
m_Peer = a_Peer;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint16_t EnetConnection::GetPeerId() const {
|
||||||
|
assert(m_Peer);
|
||||||
|
return m_Peer->GetPeerId();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EnetConnection::IsConnected() const {
|
||||||
|
if (!m_Peer)
|
||||||
|
return false;
|
||||||
|
return m_Peer->IsConnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnetConnection::Recieve(Nz::ByteArray& a_Data) {
|
||||||
|
auto packet = protocol::PacketSerializer::Deserialize(a_Data);
|
||||||
|
if (!packet)
|
||||||
|
return;
|
||||||
|
PacketDispatcher dispatcher(*this);
|
||||||
|
dispatcher.Check(*packet.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DeclarePacket(Name, NFlag, ...) \
|
||||||
|
void EnetConnection::Send##Name(const blitz::protocol::data::Name& a_##Name) const { \
|
||||||
|
m_Peer->Send(0, Nz::ENetPacketFlag::NFlag, protocol::PacketSerializer::Serialize(protocol::packets::Name(a_##Name))); \
|
||||||
|
}
|
||||||
|
|
||||||
|
DeclareAllPacket()
|
||||||
|
|
||||||
|
} // namespace network
|
||||||
|
} // namespace blitz
|
||||||
112
src/blitz/network/EnetServer.cpp
Normal file
112
src/blitz/network/EnetServer.cpp
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
#include <blitz/network/EnetServer.h>
|
||||||
|
|
||||||
|
#include <Nazara/Core/ByteStream.hpp>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace network {
|
||||||
|
|
||||||
|
EnetServer::EnetServer(std::uint16_t a_Port) : m_Running(true) {
|
||||||
|
m_Running = m_Host.Create(Nz::NetProtocol::Any, a_Port, 80);
|
||||||
|
if (m_Running) {
|
||||||
|
m_Host.AllowsIncomingConnections(true);
|
||||||
|
m_Thread = std::jthread(&EnetServer::WorkerThread, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnetServer::WorkerThread() {
|
||||||
|
while (m_Running) {
|
||||||
|
Update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EnetServer::~EnetServer() {
|
||||||
|
if (m_Running)
|
||||||
|
Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnetServer::Update() {
|
||||||
|
Nz::ENetEvent event;
|
||||||
|
int service = m_Host.Service(&event, 5);
|
||||||
|
if (service > 0) {
|
||||||
|
do {
|
||||||
|
switch (event.type) {
|
||||||
|
case Nz::ENetEventType::Disconnect: {
|
||||||
|
EnetConnection* connection = GetConnection(event.peer->GetPeerId());
|
||||||
|
if (!connection)
|
||||||
|
break;
|
||||||
|
OnClientDisconnect(*connection);
|
||||||
|
RemoveConnection(connection->GetPeerId());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Nz::ENetEventType::DisconnectTimeout: {
|
||||||
|
EnetConnection* connection = GetConnection(event.peer->GetPeerId());
|
||||||
|
if (!connection)
|
||||||
|
break;
|
||||||
|
OnClientDisconnectTimeout(*connection);
|
||||||
|
RemoveConnection(connection->GetPeerId());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Nz::ENetEventType::IncomingConnect: {
|
||||||
|
Nz::ENetPeer* peer = event.peer;
|
||||||
|
auto con = std::make_unique<EnetConnection>(peer);
|
||||||
|
m_Connections.insert({peer->GetPeerId(), std::move(con)});
|
||||||
|
OnClientConnect(*GetConnection(peer->GetPeerId()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Nz::ENetEventType::Receive: {
|
||||||
|
EnetConnection* connection = GetConnection(event.peer->GetPeerId());
|
||||||
|
if (!connection)
|
||||||
|
break;
|
||||||
|
connection->Recieve(event.packet.m_packet->data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Nz::ENetEventType::OutgoingConnect:
|
||||||
|
case Nz::ENetEventType::None:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (m_Host.CheckEvents(&event));
|
||||||
|
} else if (service < 0) {
|
||||||
|
m_Running = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EnetConnection* EnetServer::GetConnection(std::uint16_t a_PeerId) {
|
||||||
|
auto it = m_Connections.find(a_PeerId);
|
||||||
|
|
||||||
|
if (it == m_Connections.end())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return it->second.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnetServer::CloseConnection(std::uint16_t a_PeerId) {
|
||||||
|
auto connection = GetConnection(a_PeerId);
|
||||||
|
|
||||||
|
if (!connection)
|
||||||
|
return;
|
||||||
|
|
||||||
|
connection->m_Peer->DisconnectNow(0);
|
||||||
|
OnClientDisconnect(*connection);
|
||||||
|
RemoveConnection(a_PeerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnetServer::RemoveConnection(std::uint16_t a_PeerId) {
|
||||||
|
m_Connections.erase(a_PeerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnetServer::Destroy() {
|
||||||
|
m_Running = false;
|
||||||
|
m_Thread.request_stop();
|
||||||
|
m_Host.Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EnetServer::IsClosed() const {
|
||||||
|
return !m_Running;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace network
|
||||||
|
} // namespace blitz
|
||||||
28
src/blitz/protocol/PacketFactory.cpp
Normal file
28
src/blitz/protocol/PacketFactory.cpp
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#include <blitz/protocol/PacketFactory.h>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cassert>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace protocol {
|
||||||
|
namespace PacketFactory {
|
||||||
|
|
||||||
|
using PacketCreator = std::function<std::unique_ptr<Packet>()>;
|
||||||
|
|
||||||
|
#define DeclarePacket(PacketName, ...) std::make_unique<packets::PacketName>(),
|
||||||
|
|
||||||
|
static const std::array<std::unique_ptr<Packet>, static_cast<std::size_t>(PacketType::PACKET_COUNT)> Packets = {
|
||||||
|
|
||||||
|
DeclareAllPacket()
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::unique_ptr<Packet>& CreateReadOnlyPacket(PacketType a_Type) {
|
||||||
|
assert(a_Type < PacketType::PACKET_COUNT);
|
||||||
|
return Packets[static_cast<std::size_t>(a_Type)];
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace PacketFactory
|
||||||
|
} // namespace protocol
|
||||||
|
} // namespace blitz
|
||||||
12
src/blitz/protocol/PacketHandler.cpp
Normal file
12
src/blitz/protocol/PacketHandler.cpp
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#include <blitz/protocol/PacketHandler.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace protocol {
|
||||||
|
|
||||||
|
PacketHandler::PacketHandler(network::EnetConnection& a_Connection, EnttWorld& a_World) :
|
||||||
|
m_Connection(a_Connection), m_World(a_World) {}
|
||||||
|
|
||||||
|
PacketHandler::~PacketHandler() {}
|
||||||
|
|
||||||
|
} // namespace protocol
|
||||||
|
} // namespace blitz
|
||||||
289
src/blitz/protocol/PacketSerializer.cpp
Normal file
289
src/blitz/protocol/PacketSerializer.cpp
Normal file
@@ -0,0 +1,289 @@
|
|||||||
|
#include <blitz/protocol/PacketSerializer.h>
|
||||||
|
|
||||||
|
#include <Nazara/Core/ByteStream.hpp>
|
||||||
|
#include <blitz/protocol/PacketFactory.h>
|
||||||
|
#include <blitz/protocol/PacketVisitor.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace protocol {
|
||||||
|
|
||||||
|
namespace PacketSerializer {
|
||||||
|
|
||||||
|
#define DeclarePacket(PacketName, ...) \
|
||||||
|
void Visit(const packets::PacketName& a_Packet) override { \
|
||||||
|
const auto& packetData = a_Packet.m_Data; \
|
||||||
|
SerializePacketData(packetData); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
void SerializePacketData(const packets::PacketName::PacketDataType& a_Packet);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Serializer : public PacketVisitor {
|
||||||
|
private:
|
||||||
|
Nz::ByteStream& m_Buffer;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Serializer(Nz::ByteStream& a_Buffer) : m_Buffer(a_Buffer) {}
|
||||||
|
|
||||||
|
void Serialize(const Packet& a_Packet) {
|
||||||
|
m_Buffer << static_cast<PacketID>(a_Packet.GetType());
|
||||||
|
Check(a_Packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
DeclareAllPacket()
|
||||||
|
};
|
||||||
|
|
||||||
|
#undef DeclarePacket
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#define DeclarePacket(PacketName, ...) \
|
||||||
|
void Visit(const packets::PacketName& a_Packet) override { \
|
||||||
|
auto packetPtr = PacketFactory::CreatePacket<packets::PacketName>(); \
|
||||||
|
auto& packetData = packetPtr->m_Data; \
|
||||||
|
\
|
||||||
|
DeserializePacketData(packetData); \
|
||||||
|
\
|
||||||
|
m_Packet = std::move(packetPtr); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
void DeserializePacketData(packets::PacketName::PacketDataType& a_Packet);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Deserializer : public PacketVisitor {
|
||||||
|
private:
|
||||||
|
Nz::ByteStream& m_Buffer;
|
||||||
|
PacketPtr m_Packet;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Deserializer(Nz::ByteStream&& a_Buffer) : m_Buffer(a_Buffer) {}
|
||||||
|
|
||||||
|
bool Deserialize(const PacketPtr& a_Packet) {
|
||||||
|
try {
|
||||||
|
Check(*a_Packet.get());
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
PacketPtr& GetPacket() {
|
||||||
|
return m_Packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeclareAllPacket()
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Nz::ByteArray Serialize(const Packet& a_Packet) {
|
||||||
|
Nz::ByteArray buffer;
|
||||||
|
Nz::ByteStream stream(&buffer, Nz::OpenMode::Write);
|
||||||
|
|
||||||
|
Serializer serializer(stream);
|
||||||
|
serializer.Serialize(a_Packet);
|
||||||
|
|
||||||
|
buffer.ShrinkToFit();
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Packet> Deserialize(Nz::ByteArray& a_Data) {
|
||||||
|
Nz::ByteStream stream(&a_Data, Nz::OpenMode::Read);
|
||||||
|
|
||||||
|
PacketID packetId;
|
||||||
|
stream >> packetId;
|
||||||
|
|
||||||
|
if (packetId >= static_cast<PacketID>(PacketType::PACKET_COUNT))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
PacketType packetType = PacketType(packetId);
|
||||||
|
|
||||||
|
// for double-dispatch
|
||||||
|
const PacketPtr& emptyPacket = PacketFactory::CreateReadOnlyPacket(packetType);
|
||||||
|
|
||||||
|
Deserializer deserializer(std::move(stream));
|
||||||
|
if (deserializer.Deserialize(emptyPacket)) {
|
||||||
|
PacketPtr packet = std::move(deserializer.GetPacket());
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------
|
||||||
|
// Packet serializer implementation
|
||||||
|
//----------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void Serializer::SerializePacketData(const data::PlayerLogin& a_Packet) {
|
||||||
|
m_Buffer << a_Packet.m_PlayerName;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Deserializer::DeserializePacketData(data::PlayerLogin& a_Packet) {
|
||||||
|
m_Buffer >> a_Packet.m_PlayerName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void Serializer::SerializePacketData(const data::UpdateHealth& a_Packet) {
|
||||||
|
m_Buffer << a_Packet.m_NewHealth;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Deserializer::DeserializePacketData(data::UpdateHealth& a_Packet) {
|
||||||
|
m_Buffer >> a_Packet.m_NewHealth;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void Serializer::SerializePacketData(const data::LoggingSuccess& a_Packet) {
|
||||||
|
m_Buffer << a_Packet.m_PlayerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Deserializer::DeserializePacketData(data::LoggingSuccess& a_Packet) {
|
||||||
|
m_Buffer >> a_Packet.m_PlayerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void Serializer::SerializePacketData(const data::PlayerDeath& a_Packet) {}
|
||||||
|
|
||||||
|
void Deserializer::DeserializePacketData(data::PlayerDeath& a_Packet) {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void Serializer::SerializePacketData(const data::PlayerJoin& a_Packet) {
|
||||||
|
m_Buffer << a_Packet.m_Player.m_PlayerId << a_Packet.m_Player.m_Pseudo;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Deserializer::DeserializePacketData(data::PlayerJoin& a_Packet) {
|
||||||
|
m_Buffer >> a_Packet.m_Player.m_PlayerId >> a_Packet.m_Player.m_Pseudo;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void Serializer::SerializePacketData(const data::PlayerLeave& a_Packet) {
|
||||||
|
m_Buffer << a_Packet.m_PlayerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Deserializer::DeserializePacketData(data::PlayerLeave& a_Packet) {
|
||||||
|
m_Buffer >> a_Packet.m_PlayerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void Serializer::SerializePacketData(const data::PlayerList& a_Packet) {
|
||||||
|
m_Buffer << static_cast<std::uint8_t>(a_Packet.m_Players.size());
|
||||||
|
for (auto player : a_Packet.m_Players) {
|
||||||
|
m_Buffer << player.m_PlayerId << player.m_Pseudo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Deserializer::DeserializePacketData(data::PlayerList& a_Packet) {
|
||||||
|
std::uint8_t playerCount;
|
||||||
|
m_Buffer >> playerCount;
|
||||||
|
for (std::uint8_t i = 0; i < playerCount; i++) {
|
||||||
|
PlayerInfoComponent player;
|
||||||
|
m_Buffer >> player.m_PlayerId >> player.m_Pseudo;
|
||||||
|
a_Packet.m_Players.push_back(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void Serializer::SerializePacketData(const data::PlayerStats& a_Packet) {}
|
||||||
|
|
||||||
|
void Deserializer::DeserializePacketData(data::PlayerStats& a_Packet) {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void Serializer::SerializePacketData(const data::ServerConfig& a_Packet) {}
|
||||||
|
|
||||||
|
void Deserializer::DeserializePacketData(data::ServerConfig& a_Packet) {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void Serializer::SerializePacketData(const data::ServerTps& a_Packet) {}
|
||||||
|
|
||||||
|
void Deserializer::DeserializePacketData(data::ServerTps& a_Packet) {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void Serializer::SerializePacketData(const data::UpdateGameState& a_Packet) {}
|
||||||
|
|
||||||
|
void Deserializer::DeserializePacketData(data::UpdateGameState& a_Packet) {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void Serializer::SerializePacketData(const data::KeepAlive& a_Packet) {
|
||||||
|
m_Buffer << a_Packet.m_KeepAliveId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Deserializer::DeserializePacketData(data::KeepAlive& a_Packet) {
|
||||||
|
m_Buffer >> a_Packet.m_KeepAliveId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void Serializer::SerializePacketData(const data::Disconnect& a_Packet) {}
|
||||||
|
|
||||||
|
void Deserializer::DeserializePacketData(data::Disconnect& a_Packet) {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void Serializer::SerializePacketData(const data::ChatMessage& a_Packet) {
|
||||||
|
m_Buffer << a_Packet.m_Text;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Deserializer::DeserializePacketData(data::ChatMessage& a_Packet) {
|
||||||
|
m_Buffer >> a_Packet.m_Text;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void Serializer::SerializePacketData(const data::PlayerPositionAndRotation& a_Packet) {}
|
||||||
|
|
||||||
|
void Deserializer::DeserializePacketData(data::PlayerPositionAndRotation& a_Packet) {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void Serializer::SerializePacketData(const data::PlayerShoot& a_Packet) {}
|
||||||
|
|
||||||
|
void Deserializer::DeserializePacketData(data::PlayerShoot& a_Packet) {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace PacketSerializer
|
||||||
|
} // namespace protocol
|
||||||
|
} // namespace blitz
|
||||||
11
src/blitz/protocol/PacketVisitor.cpp
Normal file
11
src/blitz/protocol/PacketVisitor.cpp
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#include <blitz/protocol/PacketVisitor.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace protocol {
|
||||||
|
|
||||||
|
void PacketVisitor::Check(const Packet& a_Packet) {
|
||||||
|
a_Packet.Accept(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace protocol
|
||||||
|
} // namespace blitz
|
||||||
18
src/blitz/protocol/Packets.cpp
Normal file
18
src/blitz/protocol/Packets.cpp
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#define BLITZ_INSTANCIATE_PACKETS
|
||||||
|
#include <blitz/protocol/Packets.h>
|
||||||
|
|
||||||
|
#include <blitz/protocol/PacketVisitor.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace protocol {
|
||||||
|
|
||||||
|
template <PacketType PT, typename Data>
|
||||||
|
packets::ConcretePacket<PT, Data>::ConcretePacket(const PacketDataType& a_Data) : m_Data(a_Data) {}
|
||||||
|
|
||||||
|
template <PacketType PT, typename Data>
|
||||||
|
void packets::ConcretePacket<PT, Data>::Accept(PacketVisitor& a_Visitor) const {
|
||||||
|
a_Visitor.Visit(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace protocol
|
||||||
|
} // namespace blitz
|
||||||
19
src/blitz/systems/RemovePlayersSystem.cpp
Normal file
19
src/blitz/systems/RemovePlayersSystem.cpp
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#include <blitz/systems/RemovePlayersSystem.h>
|
||||||
|
|
||||||
|
#include <blitz/components/PlayerRemove.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
|
||||||
|
RemovePlayersSystem::RemovePlayersSystem(entt::registry&, EnttWorld& a_World) : m_World(a_World) {}
|
||||||
|
|
||||||
|
void RemovePlayersSystem::Update(Nz::Time /*elapsedTime*/) {
|
||||||
|
AtomicEnttWorld world = m_World;
|
||||||
|
|
||||||
|
entt::registry& registry = world->GetRegistry();
|
||||||
|
|
||||||
|
auto invalidPlayers = registry.view<PlayerRemoveComponent>();
|
||||||
|
|
||||||
|
registry.destroy(invalidPlayers.begin(), invalidPlayers.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace blitz
|
||||||
14
src/blitz/utils/Test.cpp
Normal file
14
src/blitz/utils/Test.cpp
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#include <blitz/utils/Test.h>
|
||||||
|
#include <blitz/common/Format.h>
|
||||||
|
#include <blitz/common/Log.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
void LogAssert(const char* expression, const char* file, int line, const char* function) {
|
||||||
|
LogE(Format("%s:%i: %s: Assertion failed !", file, line, function));
|
||||||
|
LogE(Format(" %i |\t%s;", line, expression));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace utils
|
||||||
|
} // namespace blitz
|
||||||
72
src/client/Client.cpp
Normal file
72
src/client/Client.cpp
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
#include <client/Client.h>
|
||||||
|
|
||||||
|
#include <blitz/systems/RemovePlayersSystem.h>
|
||||||
|
#include <client/handlers/KeepAliveHandler.h>
|
||||||
|
#include <client/handlers/LoggingSuccessHandler.h>
|
||||||
|
#include <client/handlers/PlayerJoinHandler.h>
|
||||||
|
#include <client/handlers/PlayerLeaveHandler.h>
|
||||||
|
#include <client/handlers/PlayerListHandler.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
Client::Client(Nz::EnttWorld& a_World) : m_World({a_World}) {
|
||||||
|
AtomicEnttWorld world = m_World;
|
||||||
|
world->AddSystem<RemovePlayersSystem>(m_World);
|
||||||
|
}
|
||||||
|
|
||||||
|
Client::~Client() {
|
||||||
|
Disconnect();
|
||||||
|
|
||||||
|
AtomicEnttWorld world = m_World;
|
||||||
|
world->RemoveSystem<RemovePlayersSystem>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::BindHandlers() {
|
||||||
|
m_NetworkClient->OnDisconnect.Connect([this]() { OnClientDisconnect(); });
|
||||||
|
m_NetworkClient->OnDisconnectTimeout.Connect([this]() { OnClientDisconnect(); });
|
||||||
|
|
||||||
|
m_Handlers.push_back(std::make_unique<KeepAliveHandler>(m_NetworkClient->GetConnection(), m_World));
|
||||||
|
m_Handlers.push_back(std::make_unique<LoggingSuccessHandler>(m_NetworkClient->GetConnection(), m_World));
|
||||||
|
auto playerJoin = std::make_unique<PlayerJoinHandler>(m_NetworkClient->GetConnection(), m_World);
|
||||||
|
playerJoin->OnLocalPlayerReady.Connect([this]() { OnClientReady(); });
|
||||||
|
m_Handlers.push_back(std::move(playerJoin));
|
||||||
|
m_Handlers.push_back(std::make_unique<PlayerLeaveHandler>(m_NetworkClient->GetConnection(), m_World));
|
||||||
|
m_Handlers.push_back(std::make_unique<PlayerListHandler>(m_NetworkClient->GetConnection(), m_World));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::UnbindHandlers() {
|
||||||
|
m_Handlers.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::Connect(const Nz::IpAddress& a_Ip) {
|
||||||
|
m_NetworkClient = std::make_unique<network::EnetClient>(a_Ip);
|
||||||
|
BindHandlers();
|
||||||
|
m_NetworkClient->OnConnect.Connect([this]() { Login(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::Disconnect() {
|
||||||
|
if (!m_NetworkClient)
|
||||||
|
return;
|
||||||
|
m_NetworkClient->GetConnection().SendDisconnect({});
|
||||||
|
m_NetworkClient->Disconnect();
|
||||||
|
UnbindHandlers();
|
||||||
|
m_NetworkClient.reset(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Client::IsConnected() {
|
||||||
|
if (!m_NetworkClient)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return m_NetworkClient->GetConnection().IsConnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::Login() {
|
||||||
|
srand(time(0));
|
||||||
|
if (!m_NetworkClient || !m_NetworkClient->GetConnection().IsConnected())
|
||||||
|
return;
|
||||||
|
m_NetworkClient->GetConnection().SendPlayerLogin({"Player_" + std::to_string(rand() % 100)});
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace blitz
|
||||||
80
src/client/ClientApp.cpp
Normal file
80
src/client/ClientApp.cpp
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
#include <client/ClientApp.h>
|
||||||
|
|
||||||
|
#include <Nazara/Core.hpp>
|
||||||
|
#include <Nazara/Core/StateMachine.hpp>
|
||||||
|
#include <Nazara/Graphics.hpp>
|
||||||
|
#include <Nazara/Physics3D.hpp>
|
||||||
|
#include <Nazara/Platform.hpp>
|
||||||
|
#include <Nazara/Renderer.hpp>
|
||||||
|
#include <Nazara/Widgets.hpp>
|
||||||
|
#include <NazaraImgui/NazaraImgui.hpp>
|
||||||
|
#include <client/states/MainMenuState.h>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
constexpr Nz::UInt32 RenderMaskUI = 0x00010000;
|
||||||
|
constexpr Nz::UInt32 RenderMask3D = 0x0000FFFF;
|
||||||
|
|
||||||
|
ClientApp::ClientApp(Nz::ApplicationBase& app) : Nz::ApplicationComponent(app), m_StateMachine(std::make_unique<Nz::StateMachine>()) {
|
||||||
|
auto& windowing = app.GetComponent<Nz::WindowingAppComponent>();
|
||||||
|
|
||||||
|
std::string windowTitle = "Blitz 2";
|
||||||
|
m_Window = &windowing.CreateWindow(Nz::VideoMode(1920, 1080, 32), windowTitle);
|
||||||
|
|
||||||
|
auto& ecs = app.GetComponent<Nz::EntitySystemAppComponent>();
|
||||||
|
|
||||||
|
auto& world = ecs.AddWorld<Nz::EnttWorld>();
|
||||||
|
|
||||||
|
Nz::RenderSystem& renderSystem = world.AddSystem<Nz::RenderSystem>();
|
||||||
|
|
||||||
|
Nz::SwapchainParameters params;
|
||||||
|
params.presentMode.clear();
|
||||||
|
params.presentMode.push_back(Nz::PresentMode::VerticalSync);
|
||||||
|
|
||||||
|
Nz::WindowSwapchain& windowSwapchain = renderSystem.CreateSwapchain(*m_Window, params);
|
||||||
|
|
||||||
|
auto renderTarget = std::make_shared<Nz::RenderWindow>(windowSwapchain);
|
||||||
|
|
||||||
|
Nz::Imgui::Instance()->Init(*m_Window, false);
|
||||||
|
|
||||||
|
auto passList = Nz::PipelinePassList::LoadFromFile("assets/example.passlist");
|
||||||
|
|
||||||
|
entt::handle cameraEntity = world.CreateEntity();
|
||||||
|
{
|
||||||
|
cameraEntity.emplace<Nz::NodeComponent>();
|
||||||
|
|
||||||
|
auto& cameraComponent = cameraEntity.emplace<Nz::CameraComponent>(renderTarget, passList, Nz::ProjectionType::Orthographic);
|
||||||
|
|
||||||
|
cameraComponent.UpdateClearColor(Nz::Color(0.0f, 0.f, .0f, 0.0f));
|
||||||
|
cameraComponent.UpdateRenderMask(RenderMaskUI);
|
||||||
|
cameraComponent.UpdateRenderOrder(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_StateData = std::make_shared<StateData>();
|
||||||
|
m_StateData->m_App = &app;
|
||||||
|
m_StateData->m_AppComponent = this;
|
||||||
|
m_StateData->m_RenderTarget = renderTarget;
|
||||||
|
m_StateData->m_Window = m_Window;
|
||||||
|
m_StateData->m_Swapchain = &windowSwapchain;
|
||||||
|
m_StateData->m_World = &world;
|
||||||
|
m_StateData->m_Canvas.emplace(
|
||||||
|
world.GetRegistry(), m_Window->GetEventHandler(), m_Window->GetCursorController().CreateHandle(), RenderMaskUI);
|
||||||
|
m_StateData->m_Canvas->Resize(Nz::Vector2f(m_Window->GetSize()));
|
||||||
|
|
||||||
|
m_StateMachine->PushState(std::make_shared<MainMenuState>(m_StateData));
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientApp::~ClientApp() {
|
||||||
|
m_StateMachine->ResetState(nullptr);
|
||||||
|
m_StateMachine->Update(Nz::Time::Zero());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientApp::Update(Nz::Time elapsedTime) {
|
||||||
|
if (!m_StateMachine->Update(elapsedTime))
|
||||||
|
GetApp().Quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace blitz
|
||||||
96
src/client/ImGuiAppComponent.cpp
Normal file
96
src/client/ImGuiAppComponent.cpp
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
#include <client/ImGuiAppComponent.h>
|
||||||
|
|
||||||
|
#include <Nazara/Core/ApplicationBase.hpp>
|
||||||
|
#include <Nazara/Platform/Window.hpp>
|
||||||
|
#include <NazaraImgui/NazaraImgui.hpp>
|
||||||
|
|
||||||
|
#include <blitz/common/Log.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
ImGuiAppComponent::ImGuiAppComponent(Nz::ApplicationBase& a_App, Nz::Window& a_Window) :
|
||||||
|
Nz::ApplicationComponent(a_App), m_Window(a_Window) {
|
||||||
|
|
||||||
|
ImGui::EnsureContextOnThisThread();
|
||||||
|
|
||||||
|
SetStyle();
|
||||||
|
|
||||||
|
a_App.AddUpdaterFunc(Nz::ApplicationBase::Interval{Nz::Time::Milliseconds(16)}, [&](Nz::Time elapsed) {
|
||||||
|
if (!m_Window.IsOpen())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_Window.ProcessEvents();
|
||||||
|
|
||||||
|
Nz::Imgui::Instance()->Update(Nz::Time::Milliseconds(16).AsSeconds());
|
||||||
|
|
||||||
|
Nz::Imgui::Instance()->Render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGuiAppComponent::~ImGuiAppComponent() {}
|
||||||
|
|
||||||
|
void ImGuiAppComponent::SetStyle() {
|
||||||
|
const static ImVec4 colorButton = {1.0f, 0.0f, 0.0f, 0.73f};
|
||||||
|
const static ImVec4 colorButtonHover = {0.56f, 0.02f, 0.02f, 1.0f};
|
||||||
|
const static ImVec4 colorButtonActive = {0.36f, 0.03f, 0.03f, 1.0f};
|
||||||
|
const static ImVec4 colorCheckMark = {1.0f, 1.0f, 1.0f, 1.0f};
|
||||||
|
const static ImVec4 colorTab = {0.38f, 0.02f, 0.02f, 1.0f};
|
||||||
|
const static ImVec4 colorTabHover = {0.74f, 0.0f, 0.0f, 0.73f};
|
||||||
|
const static ImVec4 colorTabActive = {1.0f, 0.0f, 0.0f, 0.73f};
|
||||||
|
|
||||||
|
|
||||||
|
ImGui::GetStyle().Colors[ImGuiCol_Button] = colorButton;
|
||||||
|
ImGui::GetStyle().Colors[ImGuiCol_ButtonActive] = colorButtonActive;
|
||||||
|
ImGui::GetStyle().Colors[ImGuiCol_ButtonHovered] = colorButtonHover;
|
||||||
|
ImGui::GetStyle().Colors[ImGuiCol_CheckMark] = colorCheckMark;
|
||||||
|
ImGui::GetStyle().Colors[ImGuiCol_FrameBg] = colorButton;
|
||||||
|
ImGui::GetStyle().Colors[ImGuiCol_FrameBgActive] = colorButtonActive;
|
||||||
|
ImGui::GetStyle().Colors[ImGuiCol_FrameBgHovered] = colorButtonHover;
|
||||||
|
ImGui::GetStyle().Colors[ImGuiCol_Tab] = colorTab;
|
||||||
|
ImGui::GetStyle().Colors[ImGuiCol_TabHovered] = colorTabHover;
|
||||||
|
ImGui::GetStyle().Colors[ImGuiCol_TabActive] = colorTabActive;
|
||||||
|
ImGui::GetStyle().Colors[ImGuiCol_TitleBgActive] = colorTabActive;
|
||||||
|
ImGui::GetStyle().Colors[ImGuiCol_SliderGrab] = colorButton;
|
||||||
|
ImGui::GetStyle().Colors[ImGuiCol_SliderGrabActive] = colorButton;
|
||||||
|
ImGui::GetStyle().Colors[ImGuiCol_HeaderActive] = colorTab;
|
||||||
|
ImGui::GetStyle().Colors[ImGuiCol_HeaderHovered] = colorTabActive;
|
||||||
|
ImGui::GetStyle().Colors[ImGuiCol_Header] = colorTabActive;
|
||||||
|
|
||||||
|
static const float DefaultFontSize = 25.0f;
|
||||||
|
|
||||||
|
ImFontConfig c;
|
||||||
|
c.SizePixels = DefaultFontSize;
|
||||||
|
c.OversampleH = 1; // prevent from blurring font
|
||||||
|
|
||||||
|
ImGui::GetIO().Fonts->AddFontDefault(&c);
|
||||||
|
|
||||||
|
auto* doomFont = ImGui::GetIO().Fonts->AddFontFromFileTTF("assets/fonts/doom.ttf", DefaultFontSize, &c);
|
||||||
|
ImGui::GetIO().FontDefault = doomFont;
|
||||||
|
|
||||||
|
UpdateFontTexture();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGuiAppComponent::UpdateFontTexture() {
|
||||||
|
auto& io = ImGui::GetIO();
|
||||||
|
unsigned char* pixels;
|
||||||
|
int width, height;
|
||||||
|
|
||||||
|
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
|
||||||
|
|
||||||
|
auto renderDevice = Nz::Graphics::Instance()->GetRenderDevice();
|
||||||
|
Nz::TextureInfo texParams;
|
||||||
|
texParams.width = width;
|
||||||
|
texParams.height = height;
|
||||||
|
texParams.pixelFormat = Nz::PixelFormat::RGBA8;
|
||||||
|
texParams.type = Nz::ImageType::E2D;
|
||||||
|
m_FontTexture = renderDevice->InstantiateTexture(texParams, pixels, true);
|
||||||
|
m_FontTexture->UpdateDebugName("FontTexture");
|
||||||
|
|
||||||
|
ImTextureID textureID = m_FontTexture.get();
|
||||||
|
io.Fonts->TexID = textureID;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGuiAppComponent::Update(Nz::Time elapsedTime) {}
|
||||||
|
} // namespace client
|
||||||
|
} // namespace blitz
|
||||||
18
src/client/handlers/KeepAliveHandler.cpp
Normal file
18
src/client/handlers/KeepAliveHandler.cpp
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#include <client/handlers/KeepAliveHandler.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
KeepAliveHandler::KeepAliveHandler(network::EnetConnection& a_Connection, EnttWorld& a_World) :
|
||||||
|
protocol::PacketHandler(a_Connection, a_World) {
|
||||||
|
m_Slot.Connect(m_Connection.OnKeepAlive, this, &KeepAliveHandler::Handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeepAliveHandler::Handle(const blitz::protocol::data::KeepAlive& a_KeepAlive) {
|
||||||
|
m_Connection.SendKeepAlive(a_KeepAlive);
|
||||||
|
}
|
||||||
|
|
||||||
|
KeepAliveHandler::~KeepAliveHandler() {}
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace blitz
|
||||||
22
src/client/handlers/LoggingSuccessHandler.cpp
Normal file
22
src/client/handlers/LoggingSuccessHandler.cpp
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#include <client/handlers/LoggingSuccessHandler.h>
|
||||||
|
|
||||||
|
#include <client/components/LocalPlayer.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
LoggingSuccessHandler::LoggingSuccessHandler(network::EnetConnection& a_Connection, EnttWorld& a_World) :
|
||||||
|
protocol::PacketHandler(a_Connection, a_World) {
|
||||||
|
m_Slot.Connect(m_Connection.OnLoggingSuccess, this, &LoggingSuccessHandler::Handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoggingSuccessHandler::Handle(const blitz::protocol::data::LoggingSuccess& a_LoggingSuccess) {
|
||||||
|
AtomicEnttWorld world = m_World;
|
||||||
|
auto player = world->CreateEntity();
|
||||||
|
player.emplace<LocalPlayerComponent>(a_LoggingSuccess.m_PlayerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
LoggingSuccessHandler::~LoggingSuccessHandler() {}
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace blitz
|
||||||
36
src/client/handlers/PlayerJoinHandler.cpp
Normal file
36
src/client/handlers/PlayerJoinHandler.cpp
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#include <client/handlers/PlayerJoinHandler.h>
|
||||||
|
|
||||||
|
#include <client/components/LocalPlayer.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
PlayerJoinHandler::PlayerJoinHandler(network::EnetConnection& a_Connection, EnttWorld& a_World) :
|
||||||
|
protocol::PacketHandler(a_Connection, a_World) {
|
||||||
|
m_Slot.Connect(m_Connection.OnPlayerJoin, this, &PlayerJoinHandler::Handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerJoinHandler::Handle(const protocol::data::PlayerJoin& a_PlayerJoin) {
|
||||||
|
AtomicEnttWorld world = m_World;
|
||||||
|
assert(world->GetRegistry().view<LocalPlayerComponent>().size() == 1 && "There should be only one local player !");
|
||||||
|
|
||||||
|
auto localPlayer = world->GetRegistry().view<LocalPlayerComponent>().front();
|
||||||
|
auto localPlayerId = world->GetRegistry().get<LocalPlayerComponent>(localPlayer).m_LocalPlayerId;
|
||||||
|
|
||||||
|
if (a_PlayerJoin.m_Player.m_PlayerId != localPlayerId) {
|
||||||
|
auto newPlayer = world->CreateEntity();
|
||||||
|
|
||||||
|
newPlayer.emplace<PlayerInfoComponent>(a_PlayerJoin.m_Player);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
world->GetRegistry().emplace<PlayerInfoComponent>(localPlayer, a_PlayerJoin.m_Player);
|
||||||
|
|
||||||
|
// we are now into the game so we can begin to load the world for example
|
||||||
|
OnLocalPlayerReady();
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerJoinHandler::~PlayerJoinHandler() {}
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace blitz
|
||||||
27
src/client/handlers/PlayerLeaveHandler.cpp
Normal file
27
src/client/handlers/PlayerLeaveHandler.cpp
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#include <client/handlers/PlayerLeaveHandler.h>
|
||||||
|
|
||||||
|
#include <blitz/components/PlayerRemove.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
PlayerLeaveHandler::PlayerLeaveHandler(network::EnetConnection& a_Connection, EnttWorld& a_World) :
|
||||||
|
protocol::PacketHandler(a_Connection, a_World) {
|
||||||
|
m_Slot.Connect(m_Connection.OnPlayerLeave, this, &PlayerLeaveHandler::Handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerLeaveHandler::Handle(const protocol::data::PlayerLeave& a_PlayerLeave) {
|
||||||
|
AtomicEnttWorld world = m_World;
|
||||||
|
|
||||||
|
for (auto [player, playerInfo] : world->GetRegistry().view<PlayerInfoComponent>().each()) {
|
||||||
|
if (playerInfo.m_PlayerId == a_PlayerLeave.m_PlayerId) {
|
||||||
|
world->GetRegistry().emplace<PlayerRemoveComponent>(player);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerLeaveHandler::~PlayerLeaveHandler() {}
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace blitz
|
||||||
22
src/client/handlers/PlayerListHandler.cpp
Normal file
22
src/client/handlers/PlayerListHandler.cpp
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#include <client/handlers/PlayerListHandler.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
PlayerListHandler::PlayerListHandler(network::EnetConnection& a_Connection, EnttWorld& a_World) :
|
||||||
|
protocol::PacketHandler(a_Connection, a_World) {
|
||||||
|
m_Slot.Connect(m_Connection.OnPlayerList, this, &PlayerListHandler::Handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerListHandler::Handle(const protocol::data::PlayerList& a_PlayerList) {
|
||||||
|
AtomicEnttWorld world = m_World;
|
||||||
|
for (auto playerInfo : a_PlayerList.m_Players) {
|
||||||
|
auto player = world->CreateEntity();
|
||||||
|
player.emplace<PlayerInfoComponent>(playerInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerListHandler::~PlayerListHandler() {}
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace blitz
|
||||||
57
src/client/states/AbstractState.cpp
Normal file
57
src/client/states/AbstractState.cpp
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
#include <client/states/AbstractState.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
AbstractState::AbstractState(std::shared_ptr<StateData> a_StateData) : m_StateData(std::move(a_StateData)), m_IsVisible(false) {
|
||||||
|
m_OnTargetChangeSizeSlot.Connect(
|
||||||
|
m_StateData->m_RenderTarget->OnRenderTargetSizeChange, [this](const Nz::RenderTarget*, const Nz::Vector2ui& /*newSize*/) {
|
||||||
|
if (m_IsVisible)
|
||||||
|
LayoutWidgets();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractState::~AbstractState() {
|
||||||
|
for (const auto& cleanupFunc : m_CleanupFunctions)
|
||||||
|
cleanupFunc();
|
||||||
|
|
||||||
|
for (WidgetEntry& entry : m_Widgets)
|
||||||
|
entry.m_Widget->Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractState::DestroyWidget(Nz::BaseWidget* widget) {
|
||||||
|
auto it = std::find_if(
|
||||||
|
m_Widgets.begin(), m_Widgets.end(), [&](const WidgetEntry& widgetEntity) { return widgetEntity.m_Widget == widget; });
|
||||||
|
assert(it != m_Widgets.end());
|
||||||
|
|
||||||
|
m_Widgets.erase(it);
|
||||||
|
|
||||||
|
widget->Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractState::Enter(Nz::StateMachine& /*fsm*/) {
|
||||||
|
m_IsVisible = true;
|
||||||
|
|
||||||
|
for (WidgetEntry& entry : m_Widgets) {
|
||||||
|
if (entry.m_WasVisible)
|
||||||
|
entry.m_Widget->Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
LayoutWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AbstractState::Leave(Nz::StateMachine& /*fsm*/) {
|
||||||
|
m_IsVisible = false;
|
||||||
|
|
||||||
|
for (WidgetEntry& entry : m_Widgets) {
|
||||||
|
entry.m_WasVisible = entry.m_Widget->IsVisible();
|
||||||
|
entry.m_Widget->Hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AbstractState::Update(Nz::StateMachine& /*fsm*/, Nz::Time /*elapsedTime*/) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace blitz
|
||||||
189
src/client/states/ConnectingState.cpp
Normal file
189
src/client/states/ConnectingState.cpp
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
#include "client/states/GameState.h"
|
||||||
|
#include <client/states/ConnectingState.h>
|
||||||
|
|
||||||
|
#include <Nazara/Core/ApplicationBase.hpp>
|
||||||
|
#include <Nazara/Core/Clock.hpp>
|
||||||
|
#include <Nazara/Core/EntitySystemAppComponent.hpp>
|
||||||
|
#include <Nazara/Core/EnttWorld.hpp>
|
||||||
|
#include <Nazara/Core/StateMachine.hpp>
|
||||||
|
#include <Nazara/Core/Time.hpp>
|
||||||
|
#include <Nazara/Network/Algorithm.hpp>
|
||||||
|
#include <Nazara/Network/IpAddress.hpp>
|
||||||
|
#include <Nazara/TextRenderer.hpp>
|
||||||
|
#include <Nazara/TextRenderer/SimpleTextDrawer.hpp>
|
||||||
|
#include <blitz/common/Format.h>
|
||||||
|
#include <blitz/common/Log.h>
|
||||||
|
#include <client/Client.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <server/Server.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
static const int ConnectTimeout = 5;
|
||||||
|
|
||||||
|
ConnectingState::ConnectingState(std::shared_ptr<StateData> a_StateData, std::shared_ptr<AbstractState> a_PreviousState,
|
||||||
|
const std::string& a_Address, std::uint16_t a_Port, bool a_Server) :
|
||||||
|
AbstractState(std::move(a_StateData)),
|
||||||
|
m_PreviousState(std::move(a_PreviousState)),
|
||||||
|
m_Client(std::make_unique<Client>(*GetStateData().m_World)) {
|
||||||
|
m_ResolvingData.m_Port = a_Port;
|
||||||
|
m_ResolvingData.m_ServerName = a_Address;
|
||||||
|
|
||||||
|
Nz::SimpleTextDrawer textDrawer;
|
||||||
|
textDrawer.SetTextColor({0.0, 0.0, 0.0, 1.0});
|
||||||
|
textDrawer.SetCharacterSize(75);
|
||||||
|
m_BackButton = CreateWidget<Nz::ButtonWidget>();
|
||||||
|
textDrawer.SetText("Back");
|
||||||
|
m_BackButton->UpdateText(textDrawer);
|
||||||
|
m_BackButton->Resize(m_BackButton->GetPreferredSize());
|
||||||
|
m_BackButton->OnButtonTrigger.Connect([this](const Nz::ButtonWidget*) { OnBackPressed(); });
|
||||||
|
|
||||||
|
m_StatusText = CreateWidget<Nz::LabelWidget>();
|
||||||
|
m_StatusText->UpdateText(Nz::SimpleTextDrawer::Draw("Connecting ...", 80));
|
||||||
|
|
||||||
|
if (a_Server) {
|
||||||
|
auto& app = GetStateData().m_App;
|
||||||
|
auto& ecs = app->GetComponent<Nz::EntitySystemAppComponent>();
|
||||||
|
|
||||||
|
// create server
|
||||||
|
Nz::EnttWorld& world = ecs.AddWorld<Nz::EnttWorld>();
|
||||||
|
m_Server = std::make_unique<server::Server>(a_Port, world);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ClientReady.Connect(m_Client->OnClientReady, [this]() { OnConnect(); });
|
||||||
|
m_ClientDisconnect.Connect(m_Client->OnClientDisconnect, [this]() { TryNextAddress(); });
|
||||||
|
|
||||||
|
TryResolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectingState::~ConnectingState() {}
|
||||||
|
|
||||||
|
bool ConnectingState::Update(Nz::StateMachine& fsm, Nz::Time elapsedTime) {
|
||||||
|
if (m_NextState)
|
||||||
|
fsm.ChangeState(std::move(m_NextState));
|
||||||
|
|
||||||
|
if (m_Server && m_Server->IsClosed())
|
||||||
|
OnConnectFailed();
|
||||||
|
|
||||||
|
if (m_ResolvingData.m_HasResult) {
|
||||||
|
if (m_ResolvingData.m_Result) {
|
||||||
|
// Register resolved addresses as next addresses
|
||||||
|
const auto& addresses = m_ResolvingData.m_Result.GetValue();
|
||||||
|
for (auto resultIt = addresses.rbegin(); resultIt != addresses.rend(); ++resultIt)
|
||||||
|
m_Addresses.emplace(*resultIt);
|
||||||
|
TryConnect(m_Addresses.front());
|
||||||
|
} else {
|
||||||
|
OnConnectFailed();
|
||||||
|
}
|
||||||
|
m_ResolvingData.m_HasResult = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Nz::MillisecondClock clock;
|
||||||
|
static int progress = -1;
|
||||||
|
|
||||||
|
if (m_Client && clock.RestartIfOver(Nz::Time::Milliseconds(500))) {
|
||||||
|
progress++;
|
||||||
|
progress %= 4;
|
||||||
|
std::string textStr = "Connecting ";
|
||||||
|
for (int i = 0; i < progress; i++) {
|
||||||
|
textStr += ".";
|
||||||
|
}
|
||||||
|
m_StatusText->UpdateText(Nz::SimpleTextDrawer::Draw(textStr, 80));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_Addresses.empty() && m_TimeoutClock.RestartIfOver(Nz::Time::Seconds(ConnectTimeout))) {
|
||||||
|
TryNextAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_IsConnected) {
|
||||||
|
fsm.ResetState(std::make_shared<GameState>(GetStateDataPtr(), std::move(m_Client), std::move(m_Server)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectingState::TryConnect(const Nz::IpAddress& a_ServerAddress) {
|
||||||
|
if (!m_Client)
|
||||||
|
return;
|
||||||
|
LogD("Trying to connect to " + a_ServerAddress.ToString() + " ...");
|
||||||
|
m_TimeoutClock.Restart();
|
||||||
|
m_Client->Connect(a_ServerAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectingState::TryNextAddress() {
|
||||||
|
m_Client->Disconnect();
|
||||||
|
m_Addresses.pop();
|
||||||
|
if (m_Addresses.empty())
|
||||||
|
OnConnectFailed();
|
||||||
|
else
|
||||||
|
TryConnect(m_Addresses.front());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectingState::TryResolve() {
|
||||||
|
Nz::ResolveError resolveError;
|
||||||
|
auto serverAddresses = Nz::IpAddress::ResolveHostname(
|
||||||
|
Nz::NetProtocol::Any, m_ResolvingData.m_ServerName, std::to_string(m_ResolvingData.m_Port), &resolveError);
|
||||||
|
if (serverAddresses.empty()) {
|
||||||
|
m_ResolvingData.m_Result = Nz::Err(Nz::ErrorToString(resolveError));
|
||||||
|
LogD("Failed to resolve " + m_ResolvingData.m_ServerName + " !");
|
||||||
|
} else {
|
||||||
|
std::vector<Nz::IpAddress> addresses;
|
||||||
|
addresses.reserve(serverAddresses.size());
|
||||||
|
for (auto hostnameInfo : serverAddresses) {
|
||||||
|
hostnameInfo.address.SetPort(m_ResolvingData.m_Port);
|
||||||
|
addresses.push_back(hostnameInfo.address);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ResolvingData.m_Result = std::move(addresses);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ResolvingData.m_HasResult = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectingState::LayoutWidgets() {
|
||||||
|
Nz::Vector2f canvasSize = GetStateData().m_Canvas->GetSize();
|
||||||
|
Nz::Vector2f center = canvasSize / 2.f;
|
||||||
|
|
||||||
|
constexpr float padding = 10.f;
|
||||||
|
|
||||||
|
std::array<Nz::BaseWidget*, 2> widgets = {m_StatusText, m_BackButton};
|
||||||
|
|
||||||
|
float maxWidth = 0.f;
|
||||||
|
float totalSize = padding * (widgets.size() - 1);
|
||||||
|
for (Nz::BaseWidget* widget : widgets) {
|
||||||
|
Nz::Vector2f size = widget->GetSize();
|
||||||
|
|
||||||
|
maxWidth = std::max(maxWidth, size.x);
|
||||||
|
totalSize += size.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
Nz::Vector2f cursor = center;
|
||||||
|
cursor.y += totalSize / 2.f;
|
||||||
|
|
||||||
|
for (Nz::BaseWidget* widget : widgets) {
|
||||||
|
widget->Resize({maxWidth, widget->GetHeight()});
|
||||||
|
widget->SetPosition({0.f, cursor.y - widget->GetSize().y, 0.f});
|
||||||
|
widget->CenterHorizontal();
|
||||||
|
cursor.y -= widget->GetSize().y + padding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectingState::OnBackPressed() {
|
||||||
|
m_NextState = std::move(m_PreviousState);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectingState::OnConnect() {
|
||||||
|
m_IsConnected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectingState::OnConnectFailed() {
|
||||||
|
m_StatusText->UpdateText(Nz::SimpleTextDrawer::Draw("Connection failed !", 80));
|
||||||
|
m_StatusText->CenterHorizontal();
|
||||||
|
m_Server.reset(nullptr);
|
||||||
|
m_Client.reset(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace blitz
|
||||||
108
src/client/states/CreateServerState.cpp
Normal file
108
src/client/states/CreateServerState.cpp
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
#include <Nazara/Core/Color.hpp>
|
||||||
|
#include <Nazara/Network/IpAddress.hpp>
|
||||||
|
#include <Nazara/Widgets/TextAreaWidget.hpp>
|
||||||
|
#include <client/states/CreateServerState.h>
|
||||||
|
|
||||||
|
#include <Nazara/Core/StateMachine.hpp>
|
||||||
|
#include <Nazara/TextRenderer.hpp>
|
||||||
|
#include <client/Client.h>
|
||||||
|
#include <client/states/ConnectingState.h>
|
||||||
|
#include <server/Server.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
CreateServerState::CreateServerState(std::shared_ptr<StateData> a_StateData, std::shared_ptr<AbstractState> a_PreviousState) :
|
||||||
|
AbstractState(std::move(a_StateData)), m_PreviousState(std::move(a_PreviousState)) {
|
||||||
|
Nz::SimpleTextDrawer textDrawer;
|
||||||
|
textDrawer.SetTextColor({0.0, 0.0, 0.0, 1.0});
|
||||||
|
textDrawer.SetCharacterSize(75);
|
||||||
|
|
||||||
|
m_CreateServerButton = CreateWidget<Nz::ButtonWidget>();
|
||||||
|
textDrawer.SetText("Create Server");
|
||||||
|
m_CreateServerButton->UpdateText(textDrawer);
|
||||||
|
m_CreateServerButton->Resize(m_CreateServerButton->GetPreferredSize());
|
||||||
|
m_CreateServerButton->OnButtonTrigger.Connect([this](const Nz::ButtonWidget*) { OnCreateServerPressed(); });
|
||||||
|
|
||||||
|
m_InputPort = CreateWidget<Nz::TextAreaWidget>();
|
||||||
|
m_InputPort->SetBackgroundColor(Nz::Color::White());
|
||||||
|
m_InputPort->EnableBackground(true);
|
||||||
|
m_InputPort->EnableMultiline(false);
|
||||||
|
m_InputPort->SetTextColor(Nz::Color::Black());
|
||||||
|
m_InputPort->SetCharacterFilter([](Nz::UInt32 character) {
|
||||||
|
if (character < U'0' || character > U'9')
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
m_BackButton = CreateWidget<Nz::ButtonWidget>();
|
||||||
|
textDrawer.SetText("Back");
|
||||||
|
m_BackButton->UpdateText(textDrawer);
|
||||||
|
m_BackButton->Resize(m_BackButton->GetPreferredSize());
|
||||||
|
m_BackButton->OnButtonTrigger.Connect([this](const Nz::ButtonWidget*) { OnBackPressed(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateServerState::~CreateServerState() {}
|
||||||
|
|
||||||
|
bool CreateServerState::Update(Nz::StateMachine& fsm, Nz::Time /*elapsedTime*/) {
|
||||||
|
if (m_NextState)
|
||||||
|
fsm.ChangeState(std::move(m_NextState));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateServerState::LayoutWidgets() {
|
||||||
|
Nz::Vector2f canvasSize = GetStateData().m_Canvas->GetSize();
|
||||||
|
Nz::Vector2f center = canvasSize / 2.f;
|
||||||
|
|
||||||
|
constexpr float padding = 10.f;
|
||||||
|
|
||||||
|
std::array<Nz::BaseWidget*, 3> widgets = {m_InputPort, m_CreateServerButton, m_BackButton};
|
||||||
|
|
||||||
|
float maxWidth = 0.f;
|
||||||
|
float totalSize = padding * (widgets.size() - 1);
|
||||||
|
for (Nz::BaseWidget* widget : widgets) {
|
||||||
|
Nz::Vector2f size = widget->GetSize();
|
||||||
|
|
||||||
|
maxWidth = std::max(maxWidth, size.x);
|
||||||
|
totalSize += size.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
Nz::Vector2f cursor = center;
|
||||||
|
cursor.y += totalSize / 2.f;
|
||||||
|
|
||||||
|
for (Nz::BaseWidget* widget : widgets) {
|
||||||
|
widget->Resize({maxWidth, widget->GetHeight()});
|
||||||
|
widget->SetPosition({0.f, cursor.y - widget->GetSize().y, 0.f});
|
||||||
|
widget->CenterHorizontal();
|
||||||
|
cursor.y -= widget->GetSize().y + padding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateServerState::OnCreateServerPressed() {
|
||||||
|
std::string serverPort = m_InputPort->GetText();
|
||||||
|
if (serverPort.empty()) {
|
||||||
|
// UpdateStatus("Error: blank server port", Nz::Color::Red());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long long rawPort = std::stoi(serverPort);
|
||||||
|
if (rawPort <= 0 || rawPort > 0xFFFF) {
|
||||||
|
// UpdateStatus("Error: " + serverPort + " is not a valid port", Nz::Color::Red());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Nz::IpAddress address = Nz::IpAddress::LoopbackIpV4;
|
||||||
|
address.SetPort(rawPort);
|
||||||
|
|
||||||
|
m_NextState = std::make_shared<ConnectingState>(GetStateDataPtr(), shared_from_this(), "localhost", rawPort, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateServerState::OnBackPressed() {
|
||||||
|
m_NextState = std::move(m_PreviousState);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace blitz
|
||||||
16
src/client/states/GameState.cpp
Normal file
16
src/client/states/GameState.cpp
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#include <client/states/GameState.h>
|
||||||
|
|
||||||
|
#include <client/Client.h>
|
||||||
|
#include <server/Server.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
GameState::GameState(
|
||||||
|
std::shared_ptr<StateData> a_StateData, std::unique_ptr<Client>&& a_Client, std::unique_ptr<server::Server>&& a_Server) :
|
||||||
|
AbstractState(a_StateData), m_Client(std::move(a_Client)), m_Server(std::move(a_Server)) {}
|
||||||
|
|
||||||
|
GameState::~GameState() {}
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace blitz
|
||||||
17
src/client/states/ImGuiDrawer.cpp
Normal file
17
src/client/states/ImGuiDrawer.cpp
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#include <client/states/ImGuiDrawer.h>
|
||||||
|
|
||||||
|
#include <NazaraImgui/NazaraImgui.hpp>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
ImGuiDrawer::ImGuiDrawer() {
|
||||||
|
Nz::Imgui::Instance()->AddHandler(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGuiDrawer::~ImGuiDrawer() {
|
||||||
|
Nz::Imgui::Instance()->RemoveHandler(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace blitz
|
||||||
97
src/client/states/JoinServerState.cpp
Normal file
97
src/client/states/JoinServerState.cpp
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
#include <Nazara/Network/Enums.hpp>
|
||||||
|
#include <Nazara/Network/IpAddress.hpp>
|
||||||
|
#include <client/states/ConnectingState.h>
|
||||||
|
#include <client/states/JoinServerState.h>
|
||||||
|
|
||||||
|
#include <Nazara/Core/StateMachine.hpp>
|
||||||
|
#include <Nazara/TextRenderer.hpp>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
JoinServerState::JoinServerState(std::shared_ptr<StateData> a_StateData, std::shared_ptr<AbstractState> a_PreviousState) :
|
||||||
|
AbstractState(std::move(a_StateData)), m_PreviousState(std::move(a_PreviousState)) {
|
||||||
|
Nz::SimpleTextDrawer textDrawer;
|
||||||
|
textDrawer.SetTextColor({0.0, 0.0, 0.0, 1.0});
|
||||||
|
textDrawer.SetCharacterSize(75);
|
||||||
|
|
||||||
|
m_InputAddress = CreateWidget<Nz::TextAreaWidget>();
|
||||||
|
m_InputAddress->SetBackgroundColor(Nz::Color::White());
|
||||||
|
m_InputAddress->EnableBackground(true);
|
||||||
|
m_InputAddress->EnableMultiline(false);
|
||||||
|
m_InputAddress->SetTextColor(Nz::Color::Black());
|
||||||
|
m_InputAddress->SetText("localhost");
|
||||||
|
|
||||||
|
m_JoinServerButton = CreateWidget<Nz::ButtonWidget>();
|
||||||
|
textDrawer.SetText("Join Server");
|
||||||
|
m_JoinServerButton->UpdateText(textDrawer);
|
||||||
|
m_JoinServerButton->Resize(m_JoinServerButton->GetPreferredSize());
|
||||||
|
m_JoinServerButton->OnButtonTrigger.Connect([this](const Nz::ButtonWidget*) { OnJoinServerPressed(); });
|
||||||
|
|
||||||
|
m_BackButton = CreateWidget<Nz::ButtonWidget>();
|
||||||
|
textDrawer.SetText("Back");
|
||||||
|
m_BackButton->UpdateText(textDrawer);
|
||||||
|
m_BackButton->Resize(m_BackButton->GetPreferredSize());
|
||||||
|
m_BackButton->OnButtonTrigger.Connect([this](const Nz::ButtonWidget*) { OnBackPressed(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
JoinServerState::~JoinServerState() {}
|
||||||
|
|
||||||
|
bool JoinServerState::Update(Nz::StateMachine& fsm, Nz::Time /*elapsedTime*/) {
|
||||||
|
if (m_NextState)
|
||||||
|
fsm.ChangeState(std::move(m_NextState));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JoinServerState::LayoutWidgets() {
|
||||||
|
Nz::Vector2f canvasSize = GetStateData().m_Canvas->GetSize();
|
||||||
|
Nz::Vector2f center = canvasSize / 2.f;
|
||||||
|
|
||||||
|
constexpr float padding = 10.f;
|
||||||
|
|
||||||
|
std::array<Nz::BaseWidget*, 3> widgets = {m_InputAddress, m_JoinServerButton, m_BackButton};
|
||||||
|
|
||||||
|
float maxWidth = 0.f;
|
||||||
|
float totalSize = padding * (widgets.size() - 1);
|
||||||
|
for (Nz::BaseWidget* widget : widgets) {
|
||||||
|
Nz::Vector2f size = widget->GetSize();
|
||||||
|
|
||||||
|
maxWidth = std::max(maxWidth, size.x);
|
||||||
|
totalSize += size.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
Nz::Vector2f cursor = center;
|
||||||
|
cursor.y += totalSize / 2.f;
|
||||||
|
|
||||||
|
for (Nz::BaseWidget* widget : widgets) {
|
||||||
|
widget->Resize({maxWidth, widget->GetHeight()});
|
||||||
|
widget->SetPosition({0.f, cursor.y - widget->GetSize().y, 0.f});
|
||||||
|
widget->CenterHorizontal();
|
||||||
|
cursor.y -= widget->GetSize().y + padding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void JoinServerState::OnJoinServerPressed() {
|
||||||
|
std::string address = m_InputAddress->GetText();
|
||||||
|
auto separator = address.find(':');
|
||||||
|
std::string name = address.substr(0, separator);
|
||||||
|
std::uint16_t port = 25565;
|
||||||
|
if (separator != std::string::npos) {
|
||||||
|
try {
|
||||||
|
std::string rawPort = address.substr(separator + 1, std::string::npos);
|
||||||
|
port = std::stoi(rawPort);
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_NextState = std::make_shared<ConnectingState>(GetStateDataPtr(), shared_from_this(), name, port, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JoinServerState::OnBackPressed() {
|
||||||
|
m_NextState = std::move(m_PreviousState);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace blitz
|
||||||
108
src/client/states/MainMenuState.cpp
Normal file
108
src/client/states/MainMenuState.cpp
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
#include <client/states/MainMenuState.h>
|
||||||
|
|
||||||
|
#include <Nazara/Core/ApplicationBase.hpp>
|
||||||
|
#include <Nazara/Core/StateMachine.hpp>
|
||||||
|
#include <Nazara/TextRenderer.hpp>
|
||||||
|
#include <NazaraImgui/ImguiHandler.hpp>
|
||||||
|
#include <NazaraImgui/NazaraImgui.hpp>
|
||||||
|
#include <client/states/CreateServerState.h>
|
||||||
|
#include <client/states/JoinServerState.h>
|
||||||
|
#include <client/states/OptionState.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
MainMenuState::MainMenuState(std::shared_ptr<StateData> a_StateData) : AbstractState(std::move(a_StateData)) {
|
||||||
|
Nz::SimpleTextDrawer textDrawer;
|
||||||
|
textDrawer.SetTextColor({0.0, 0.0, 0.0, 1.0});
|
||||||
|
textDrawer.SetCharacterSize(75);
|
||||||
|
|
||||||
|
m_JoinServerButton = CreateWidget<Nz::ButtonWidget>();
|
||||||
|
textDrawer.SetText("Join Server");
|
||||||
|
m_JoinServerButton->UpdateText(textDrawer);
|
||||||
|
m_JoinServerButton->Resize(m_JoinServerButton->GetPreferredSize());
|
||||||
|
m_JoinServerButton->OnButtonTrigger.Connect([this](const Nz::ButtonWidget*) { OnJoinServerPressed(); });
|
||||||
|
|
||||||
|
m_CreateServerButton = CreateWidget<Nz::ButtonWidget>();
|
||||||
|
textDrawer.SetText("Create Server");
|
||||||
|
m_CreateServerButton->UpdateText(textDrawer);
|
||||||
|
m_CreateServerButton->Resize(m_CreateServerButton->GetPreferredSize());
|
||||||
|
m_CreateServerButton->OnButtonTrigger.Connect([this](const Nz::ButtonWidget*) { OnCreateServerPressed(); });
|
||||||
|
|
||||||
|
m_OptionButton = CreateWidget<Nz::ButtonWidget>();
|
||||||
|
textDrawer.SetText("Settings");
|
||||||
|
m_OptionButton->UpdateText(textDrawer);
|
||||||
|
m_OptionButton->Resize(m_OptionButton->GetPreferredSize());
|
||||||
|
m_OptionButton->OnButtonTrigger.Connect([this](const Nz::ButtonWidget*) { OnOptionPressed(); });
|
||||||
|
|
||||||
|
m_QuitButton = CreateWidget<Nz::ButtonWidget>();
|
||||||
|
textDrawer.SetText("Quit game");
|
||||||
|
m_QuitButton->UpdateText(textDrawer);
|
||||||
|
m_QuitButton->Resize(m_QuitButton->GetPreferredSize());
|
||||||
|
m_QuitButton->OnButtonTrigger.Connect([this](const Nz::ButtonWidget*) { OnQuitPressed(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
MainMenuState::~MainMenuState() {}
|
||||||
|
|
||||||
|
bool MainMenuState::Update(Nz::StateMachine& fsm, Nz::Time elapsedTime) {
|
||||||
|
if (!AbstractState::Update(fsm, elapsedTime))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (m_NextState)
|
||||||
|
fsm.ChangeState(std::move(m_NextState));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainMenuState::LayoutWidgets() {
|
||||||
|
Nz::Vector2f canvasSize = GetStateData().m_Canvas->GetSize();
|
||||||
|
Nz::Vector2f center = canvasSize / 2.f;
|
||||||
|
|
||||||
|
constexpr float padding = 10.f;
|
||||||
|
|
||||||
|
std::array<Nz::BaseWidget*, 4> widgets = {m_JoinServerButton, m_CreateServerButton, m_OptionButton, m_QuitButton};
|
||||||
|
|
||||||
|
float maxWidth = 0.f;
|
||||||
|
float totalSize = padding * (widgets.size() - 1);
|
||||||
|
for (Nz::BaseWidget* widget : widgets) {
|
||||||
|
Nz::Vector2f size = widget->GetSize();
|
||||||
|
|
||||||
|
maxWidth = std::max(maxWidth, size.x);
|
||||||
|
totalSize += size.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
Nz::Vector2f cursor = center;
|
||||||
|
cursor.y += totalSize / 2.f;
|
||||||
|
|
||||||
|
for (Nz::BaseWidget* widget : widgets) {
|
||||||
|
widget->Resize({maxWidth, widget->GetHeight()});
|
||||||
|
widget->SetPosition({0.f, cursor.y - widget->GetSize().y, 0.f});
|
||||||
|
widget->CenterHorizontal();
|
||||||
|
cursor.y -= widget->GetSize().y + padding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainMenuState::OnRenderImgui() {
|
||||||
|
#ifndef NDEBUG
|
||||||
|
ImGui::ShowDemoWindow(nullptr);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainMenuState::OnJoinServerPressed() {
|
||||||
|
m_NextState = std::make_shared<JoinServerState>(GetStateDataPtr(), shared_from_this());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainMenuState::OnCreateServerPressed() {
|
||||||
|
m_NextState = std::make_shared<CreateServerState>(GetStateDataPtr(), shared_from_this());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainMenuState::OnOptionPressed() {
|
||||||
|
m_NextState = std::make_shared<OptionState>(GetStateDataPtr(), shared_from_this());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainMenuState::OnQuitPressed() {
|
||||||
|
GetStateData().m_App->Quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace blitz
|
||||||
73
src/client/states/OptionState.cpp
Normal file
73
src/client/states/OptionState.cpp
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
#include <client/states/OptionState.h>
|
||||||
|
|
||||||
|
#include <Nazara/Core/StateMachine.hpp>
|
||||||
|
#include <Nazara/TextRenderer.hpp>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
OptionState::OptionState(std::shared_ptr<StateData> a_StateData, std::shared_ptr<AbstractState> a_PreviousState) :
|
||||||
|
AbstractState(std::move(a_StateData)), m_PreviousState(std::move(a_PreviousState)) {
|
||||||
|
Nz::SimpleTextDrawer textDrawer;
|
||||||
|
textDrawer.SetTextColor({0.0, 0.0, 0.0, 1.0});
|
||||||
|
textDrawer.SetCharacterSize(75);
|
||||||
|
|
||||||
|
m_OptionButton = CreateWidget<Nz::ButtonWidget>();
|
||||||
|
textDrawer.SetText("Settings");
|
||||||
|
m_OptionButton->UpdateText(textDrawer);
|
||||||
|
m_OptionButton->Resize(m_OptionButton->GetPreferredSize());
|
||||||
|
m_OptionButton->OnButtonTrigger.Connect([this](const Nz::ButtonWidget*) { OnOptionPressed(); });
|
||||||
|
|
||||||
|
m_BackButton = CreateWidget<Nz::ButtonWidget>();
|
||||||
|
textDrawer.SetText("Back");
|
||||||
|
m_BackButton->UpdateText(textDrawer);
|
||||||
|
m_BackButton->Resize(m_BackButton->GetPreferredSize());
|
||||||
|
m_BackButton->OnButtonTrigger.Connect([this](const Nz::ButtonWidget*) { OnBackPressed(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
OptionState::~OptionState() {}
|
||||||
|
|
||||||
|
void OptionState::LayoutWidgets() {
|
||||||
|
Nz::Vector2f canvasSize = GetStateData().m_Canvas->GetSize();
|
||||||
|
Nz::Vector2f center = canvasSize / 2.f;
|
||||||
|
|
||||||
|
constexpr float padding = 10.f;
|
||||||
|
|
||||||
|
std::array<Nz::BaseWidget*, 2> widgets = {m_OptionButton, m_BackButton};
|
||||||
|
|
||||||
|
float maxWidth = 0.f;
|
||||||
|
float totalSize = padding * (widgets.size() - 1);
|
||||||
|
for (Nz::BaseWidget* widget : widgets) {
|
||||||
|
Nz::Vector2f size = widget->GetSize();
|
||||||
|
|
||||||
|
maxWidth = std::max(maxWidth, size.x);
|
||||||
|
totalSize += size.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
Nz::Vector2f cursor = center;
|
||||||
|
cursor.y += totalSize / 2.f;
|
||||||
|
|
||||||
|
for (Nz::BaseWidget* widget : widgets) {
|
||||||
|
widget->Resize({maxWidth, widget->GetHeight()});
|
||||||
|
widget->SetPosition({0.f, cursor.y - widget->GetSize().y, 0.f});
|
||||||
|
widget->CenterHorizontal();
|
||||||
|
cursor.y -= widget->GetSize().y + padding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OptionState::Update(Nz::StateMachine& fsm, Nz::Time /*elapsedTime*/) {
|
||||||
|
if (m_NextState)
|
||||||
|
fsm.ChangeState(std::move(m_NextState));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OptionState::OnOptionPressed() {}
|
||||||
|
|
||||||
|
void OptionState::OnBackPressed() {
|
||||||
|
m_NextState = std::move(m_PreviousState);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace blitz
|
||||||
260
src/main.cpp
260
src/main.cpp
@@ -1,260 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include <Nazara/Core.hpp>
|
|
||||||
#include <Nazara/Graphics.hpp>
|
|
||||||
#include <Nazara/Renderer.hpp>
|
|
||||||
#include <Nazara/Platform.hpp>
|
|
||||||
#include <Nazara/Physics3D.hpp>
|
|
||||||
#include <random>
|
|
||||||
|
|
||||||
static Nz::Vector3f Get2DDirectionVectorFromRotation(float yaw) {
|
|
||||||
return {
|
|
||||||
-std::sin(yaw),
|
|
||||||
0,
|
|
||||||
-std::cos(yaw),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static void CreateLight(Nz::EnttWorld &world)
|
|
||||||
{
|
|
||||||
entt::handle lightEntity = world.CreateEntity();
|
|
||||||
{
|
|
||||||
auto &lightNode = lightEntity.emplace<Nz::NodeComponent>();
|
|
||||||
lightNode.SetPosition({0, 5, 0});
|
|
||||||
|
|
||||||
auto &entityLight = lightEntity.emplace<Nz::LightComponent>();
|
|
||||||
auto &spotLight = entityLight.AddLight<Nz::PointLight>(1);
|
|
||||||
spotLight.EnableShadowCasting(true);
|
|
||||||
spotLight.UpdateShadowMapSize(1024);
|
|
||||||
spotLight.UpdateRadius(10.0f);
|
|
||||||
spotLight.UpdateDiffuseFactor(0.5f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void CreateBoxes(Nz::EnttWorld &world)
|
|
||||||
{
|
|
||||||
constexpr float BoxDims = 1.f;
|
|
||||||
|
|
||||||
std::mt19937 rd(42);
|
|
||||||
std::uniform_real_distribution<float> colorDis(0.f, 360.f);
|
|
||||||
std::uniform_real_distribution<float> radiusDis(0.1f, 0.5f);
|
|
||||||
|
|
||||||
std::uniform_real_distribution<float> lengthDis(0.2f, 1.5f);
|
|
||||||
std::shared_ptr<Nz::GraphicalMesh> boxMesh = Nz::GraphicalMesh::Build(Nz::Primitive::Box(Nz::Vector3f(1.f)));
|
|
||||||
|
|
||||||
constexpr std::size_t BoxCount = 100;
|
|
||||||
for (std::size_t i = 0; i < BoxCount; ++i)
|
|
||||||
{
|
|
||||||
float width = lengthDis(rd);
|
|
||||||
float height = lengthDis(rd);
|
|
||||||
float depth = lengthDis(rd);
|
|
||||||
|
|
||||||
std::uniform_real_distribution<float> xRandom(-BoxDims * 0.5f + width, BoxDims * 0.5f - width);
|
|
||||||
std::uniform_real_distribution<float> yRandom(-BoxDims * 0.5f + height, BoxDims * 0.5f - height);
|
|
||||||
std::uniform_real_distribution<float> zRandom(-BoxDims * 0.5f + depth, BoxDims * 0.5f - depth);
|
|
||||||
|
|
||||||
entt::handle boxEntity = world.CreateEntity();
|
|
||||||
|
|
||||||
std::shared_ptr<Nz::MaterialInstance> boxMaterial = Nz::MaterialInstance::Instantiate(Nz::MaterialType::Phong);
|
|
||||||
boxMaterial->SetValueProperty("BaseColor", Nz::Color::sRGBToLinear(Nz::Color::FromHSV(colorDis(rd), 1.f, 1.f)));
|
|
||||||
|
|
||||||
std::shared_ptr<Nz::Model> sphereModel = std::make_shared<Nz::Model>(boxMesh);
|
|
||||||
sphereModel->SetMaterial(0, std::move(boxMaterial));
|
|
||||||
|
|
||||||
boxEntity.emplace<Nz::GraphicsComponent>(std::move(sphereModel));
|
|
||||||
|
|
||||||
auto &ballNode = boxEntity.emplace<Nz::NodeComponent>();
|
|
||||||
ballNode.SetPosition({xRandom(rd), yRandom(rd) + 20.0f, zRandom(rd)});
|
|
||||||
ballNode.SetScale({width, height, depth});
|
|
||||||
|
|
||||||
std::shared_ptr<Nz::BoxCollider3D> boxCollider = std::make_shared<Nz::BoxCollider3D>(Nz::Vector3f(width, height, depth));
|
|
||||||
|
|
||||||
Nz::RigidBody3D::DynamicSettings settings;
|
|
||||||
settings.geom = boxCollider;
|
|
||||||
settings.mass = width * height * depth;
|
|
||||||
|
|
||||||
boxEntity.emplace<Nz::RigidBody3DComponent>(settings);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void CreateModel(Nz::EnttWorld &world)
|
|
||||||
{
|
|
||||||
std::filesystem::path resourceDir = "assets/models";
|
|
||||||
|
|
||||||
Nz::MeshParams meshParams;
|
|
||||||
meshParams.center = true;
|
|
||||||
meshParams.vertexRotation = Nz::EulerAnglesf(0.f, 0.f, 0.f);
|
|
||||||
meshParams.vertexScale = Nz::Vector3f(1.0f);
|
|
||||||
meshParams.vertexDeclaration = Nz::VertexDeclaration::Get(Nz::VertexLayout::XYZ_Normal_UV_Tangent);
|
|
||||||
|
|
||||||
std::shared_ptr<Nz::Mesh> deambuMesh = Nz::Mesh::LoadFromFile(resourceDir / "sol.obj", meshParams);
|
|
||||||
if (!deambuMesh)
|
|
||||||
{
|
|
||||||
NazaraError("failed to load model");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Nz::GraphicalMesh> gfxMesh = Nz::GraphicalMesh::BuildFromMesh(*deambuMesh);
|
|
||||||
std::shared_ptr<Nz::Model> deambModel = std::make_shared<Nz::Model>(std::move(gfxMesh));
|
|
||||||
|
|
||||||
entt::handle deambEntity = world.CreateEntity();
|
|
||||||
{
|
|
||||||
auto &entityGfx = deambEntity.emplace<Nz::GraphicsComponent>();
|
|
||||||
entityGfx.AttachRenderable(deambModel);
|
|
||||||
|
|
||||||
auto &entityNode = deambEntity.emplace<Nz::NodeComponent>();
|
|
||||||
entityNode.SetPosition(Nz::Vector3f(0.f, 0.f, 0.f));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Nz::RenderDevice> device = Nz::Graphics::Instance()->GetRenderDevice();
|
|
||||||
|
|
||||||
std::shared_ptr<Nz::MaterialInstance> material = Nz::MaterialInstance::Instantiate(Nz::MaterialType::Phong);
|
|
||||||
for (std::string_view passName : {"DepthPass", "ForwardPass"})
|
|
||||||
{
|
|
||||||
material->UpdatePassStates(passName, [](Nz::RenderStates &states)
|
|
||||||
{
|
|
||||||
states.depthClamp = true;
|
|
||||||
return true; });
|
|
||||||
}
|
|
||||||
|
|
||||||
std::mt19937 rd(42);
|
|
||||||
std::uniform_real_distribution<float> colorDis(0.f, 360.f);
|
|
||||||
material->SetValueProperty("BaseColor", Nz::Color::sRGBToLinear(Nz::Color::FromHSV(colorDis(rd), 1.f, 1.f)));
|
|
||||||
|
|
||||||
for (std::size_t i = 0; i < deambModel->GetSubMeshCount(); ++i)
|
|
||||||
deambModel->SetMaterial(i, material);
|
|
||||||
|
|
||||||
Nz::VertexMapper vertexMapper(*deambuMesh->GetSubMesh(0));
|
|
||||||
Nz::SparsePtr<Nz::Vector3f> vertices = vertexMapper.GetComponentPtr<Nz::Vector3f>(Nz::VertexComponent::Position);
|
|
||||||
|
|
||||||
auto shipCollider = std::make_shared<Nz::ConvexHullCollider3D>(vertices, vertexMapper.GetVertexCount(), 0.1f);
|
|
||||||
|
|
||||||
Nz::RigidBody3D::StaticSettings settings;
|
|
||||||
settings.geom = shipCollider;
|
|
||||||
|
|
||||||
deambEntity.emplace<Nz::RigidBody3DComponent>(settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Nz::EulerAnglesf camAngles(0.f, 0.f, 0.f);
|
|
||||||
|
|
||||||
static void CreateCamera(Nz::EnttWorld &world, Nz::Window &window, Nz::Application<Nz::Graphics, Nz::Physics3D> &app)
|
|
||||||
{
|
|
||||||
Nz::RenderSystem &renderSystem = world.AddSystem<Nz::RenderSystem>();
|
|
||||||
|
|
||||||
Nz::SwapchainParameters params;
|
|
||||||
params.presentMode.clear();
|
|
||||||
params.presentMode.push_back(Nz::PresentMode::VerticalSync);
|
|
||||||
|
|
||||||
Nz::WindowSwapchain &windowSwapchain = renderSystem.CreateSwapchain(window, params);
|
|
||||||
|
|
||||||
// Création de la caméra
|
|
||||||
entt::handle cameraEntity = world.CreateEntity();
|
|
||||||
|
|
||||||
auto &cameraNode = cameraEntity.emplace<Nz::NodeComponent>();
|
|
||||||
cameraNode.SetPosition({0, 1, 0});
|
|
||||||
|
|
||||||
auto &cameraComponent = cameraEntity.emplace<Nz::CameraComponent>(std::make_shared<Nz::RenderWindow>(windowSwapchain), Nz::ProjectionType::Perspective);
|
|
||||||
cameraComponent.UpdateClearColor(Nz::Color(0.3f, 0.8f, 1.0f));
|
|
||||||
cameraComponent.UpdateZNear(0.1f);
|
|
||||||
cameraComponent.UpdateZFar(100.0f);
|
|
||||||
|
|
||||||
entt::handle playerEntity = world.CreateEntity();
|
|
||||||
|
|
||||||
auto &playerNode = playerEntity.emplace<Nz::NodeComponent>();
|
|
||||||
playerNode.SetPosition({0, 5, 0});
|
|
||||||
cameraNode.SetParent(playerEntity);
|
|
||||||
|
|
||||||
window.GetEventHandler().OnMouseMoved.Connect([&](const Nz::WindowEventHandler * /*eventHandler*/, const Nz::WindowEvent::MouseMoveEvent &event)
|
|
||||||
{
|
|
||||||
constexpr float sensitivity = 0.3f;
|
|
||||||
|
|
||||||
camAngles.yaw -= event.deltaX * sensitivity;
|
|
||||||
camAngles.yaw.Normalize();
|
|
||||||
|
|
||||||
camAngles.pitch = Nz::Clamp(camAngles.pitch - event.deltaY * sensitivity, -89.f, 89.f);
|
|
||||||
|
|
||||||
camAngles.roll = 0.0f;
|
|
||||||
|
|
||||||
cameraNode.SetRotation(camAngles);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
std::shared_ptr<Nz::BoxCollider3D> boxCollider = std::make_shared<Nz::BoxCollider3D>(Nz::Vector3f(1, 2, 1));
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static const int playerMass = 100000;
|
|
||||||
|
|
||||||
Nz::RigidBody3D::DynamicSettings settings;
|
|
||||||
settings.geom = boxCollider;
|
|
||||||
settings.mass = playerMass;
|
|
||||||
auto &playerBody = playerEntity.emplace<Nz::RigidBody3DComponent>(settings);
|
|
||||||
|
|
||||||
app.AddUpdaterFunc([&](){
|
|
||||||
static Nz::MillisecondClock updateClock;
|
|
||||||
if (std::optional<Nz::Time> deltaTime = updateClock.RestartIfOver(Nz::Time::TickDuration(60)))
|
|
||||||
{
|
|
||||||
camAngles.roll = 0;
|
|
||||||
cameraNode.SetRotation(camAngles);
|
|
||||||
|
|
||||||
Nz::Vector3f front = Get2DDirectionVectorFromRotation(camAngles.yaw.ToRadians());
|
|
||||||
Nz::Vector3f left = Get2DDirectionVectorFromRotation(camAngles.yaw.ToRadians() + 3.1415926535/2.0);
|
|
||||||
|
|
||||||
if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::Z))
|
|
||||||
playerBody.AddForce(front * 10.f * playerMass, Nz::CoordSys::Local);
|
|
||||||
|
|
||||||
if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::S))
|
|
||||||
playerBody.AddForce(-front * 10.f * playerMass, Nz::CoordSys::Local);
|
|
||||||
|
|
||||||
if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::Q))
|
|
||||||
playerBody.AddForce(left * 10.f * playerMass, Nz::CoordSys::Local);
|
|
||||||
|
|
||||||
if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::D))
|
|
||||||
playerBody.AddForce(-left * 10.f * playerMass, Nz::CoordSys::Local);
|
|
||||||
|
|
||||||
if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::Space))
|
|
||||||
playerBody.AddForce(Nz::Vector3f::Up() * 15.f * playerMass, Nz::CoordSys::Local);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
Nz::Application<Nz::Graphics, Nz::Physics3D> app(argc, argv);
|
|
||||||
|
|
||||||
auto &windowing = app.AddComponent<Nz::WindowingAppComponent>();
|
|
||||||
|
|
||||||
std::string windowTitle = "Blitz 2";
|
|
||||||
Nz::Window &window = windowing.CreateWindow(Nz::VideoMode(1920, 1080, 32), windowTitle);
|
|
||||||
|
|
||||||
auto &ecs = app.AddComponent<Nz::EntitySystemAppComponent>();
|
|
||||||
|
|
||||||
auto &world = ecs.AddWorld<Nz::EnttWorld>();
|
|
||||||
|
|
||||||
auto &physSystem = world.AddSystem<Nz::Physics3DSystem>();
|
|
||||||
physSystem.GetPhysWorld().SetMaxStepCount(1);
|
|
||||||
physSystem.GetPhysWorld().SetStepSize(Nz::Time::TickDuration(50));
|
|
||||||
physSystem.GetPhysWorld().SetGravity(Nz::Vector3f::Down() * 9.81f);
|
|
||||||
|
|
||||||
CreateCamera(world, window, app);
|
|
||||||
CreateBoxes(world);
|
|
||||||
CreateModel(world);
|
|
||||||
CreateLight(world);
|
|
||||||
|
|
||||||
Nz::Mouse::SetRelativeMouseMode(true);
|
|
||||||
|
|
||||||
Nz::MillisecondClock fpsClock;
|
|
||||||
unsigned int fps = 0;
|
|
||||||
|
|
||||||
app.AddUpdaterFunc([&]()
|
|
||||||
{
|
|
||||||
fps++;
|
|
||||||
|
|
||||||
if (fpsClock.RestartIfOver(Nz::Time::Second()))
|
|
||||||
{
|
|
||||||
window.SetTitle(windowTitle + " - " + Nz::NumberToString(fps) + " FPS" + " - " + Nz::NumberToString(world.GetAliveEntityCount()) + " entities");
|
|
||||||
fps = 0;
|
|
||||||
} });
|
|
||||||
|
|
||||||
return app.Run();
|
|
||||||
}
|
|
||||||
94
src/server/Server.cpp
Normal file
94
src/server/Server.cpp
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
#include <server/Server.h>
|
||||||
|
|
||||||
|
#include <server/components/EnetConnection.h>
|
||||||
|
#include <server/components/ServerIdCounter.h>
|
||||||
|
|
||||||
|
#include <server/handlers/KeepAliveHandler.h>
|
||||||
|
#include <server/handlers/PlayerLoginHandler.h>
|
||||||
|
|
||||||
|
#include <blitz/systems/RemovePlayersSystem.h>
|
||||||
|
#include <server/systems/DisconnectSystem.h>
|
||||||
|
#include <server/systems/KeepAliveSystem.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
Server::Server(std::uint16_t a_Port, Nz::EnttWorld& a_World) : m_World({a_World}), m_NetworkServer(a_Port) {
|
||||||
|
RegisterSystems();
|
||||||
|
m_NetworkServer.OnClientConnect.Connect(this, &Server::HandleConnect);
|
||||||
|
m_NetworkServer.OnClientDisconnect.Connect(this, &Server::HandleDisconnect);
|
||||||
|
m_NetworkServer.OnClientDisconnectTimeout.Connect(this, &Server::HandleDisconnect);
|
||||||
|
}
|
||||||
|
|
||||||
|
Server::~Server() {
|
||||||
|
CloseServer();
|
||||||
|
|
||||||
|
AtomicEnttWorld world = m_World;
|
||||||
|
world->RemoveSystem<KeepAliveSystem>();
|
||||||
|
world->RemoveSystem<DisconnectSystem>();
|
||||||
|
world->RemoveSystem<RemovePlayersSystem>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::HandleConnect(network::EnetConnection& a_Connection) {
|
||||||
|
Session newSession;
|
||||||
|
newSession.m_Connection = &a_Connection;
|
||||||
|
|
||||||
|
RegisterHandlers(newSession);
|
||||||
|
|
||||||
|
m_Sessions.insert({a_Connection.GetPeerId(), std::move(newSession)});
|
||||||
|
|
||||||
|
CreateEntity(a_Connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::HandleDisconnect(network::EnetConnection& a_Connection) {
|
||||||
|
m_Sessions.erase(a_Connection.GetPeerId());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::CreateEntity(network::EnetConnection& a_Connection) {
|
||||||
|
AtomicEnttWorld world = m_World;
|
||||||
|
|
||||||
|
auto entity = world->CreateEntity();
|
||||||
|
|
||||||
|
entity.emplace<EnetConnectionComponent>(&a_Connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::RegisterSystems() {
|
||||||
|
AtomicEnttWorld world = m_World;
|
||||||
|
world->AddSystem<KeepAliveSystem>(m_World);
|
||||||
|
world->AddSystem<DisconnectSystem>(m_World, *this);
|
||||||
|
world->AddSystem<RemovePlayersSystem>(m_World);
|
||||||
|
|
||||||
|
auto counter = world->CreateEntity();
|
||||||
|
counter.emplace<ServerIdCounterComponent>(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::RegisterHandlers(Session& session) {
|
||||||
|
session.m_Handlers.push_back(std::make_unique<KeepAliveHandler>(*session.m_Connection, m_World));
|
||||||
|
session.m_Handlers.push_back(std::make_unique<PlayerLoginHandler>(*session.m_Connection, m_World));
|
||||||
|
}
|
||||||
|
|
||||||
|
network::EnetConnection* Server::GetConnection(std::uint16_t a_PeerId) {
|
||||||
|
auto it = m_Sessions.find(a_PeerId);
|
||||||
|
|
||||||
|
if (it == m_Sessions.end())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return it->second.m_Connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::CloseConnection(std::uint16_t a_PeerId) {
|
||||||
|
m_NetworkServer.CloseConnection(a_PeerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::CloseServer() {
|
||||||
|
for (auto& [peerId, session] : m_Sessions) {
|
||||||
|
CloseConnection(peerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Server::IsClosed() const {
|
||||||
|
return m_NetworkServer.IsClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace server
|
||||||
|
} // namespace blitz
|
||||||
29
src/server/handlers/KeepAliveHandler.cpp
Normal file
29
src/server/handlers/KeepAliveHandler.cpp
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#include <server/handlers/KeepAliveHandler.h>
|
||||||
|
|
||||||
|
#include <server/components/KeepAliveSession.h>
|
||||||
|
|
||||||
|
#include <Nazara/Core/Time.hpp>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
KeepAliveHandler::KeepAliveHandler(network::EnetConnection& a_Connection, EnttWorld& a_World) :
|
||||||
|
protocol::PacketHandler(a_Connection, a_World) {
|
||||||
|
m_Slot.Connect(a_Connection.OnKeepAlive,
|
||||||
|
[this](const protocol::data::KeepAlive& a_KeepAlive) { Handle(m_Connection.GetPeerId(), a_KeepAlive); });
|
||||||
|
}
|
||||||
|
|
||||||
|
KeepAliveHandler::~KeepAliveHandler() {}
|
||||||
|
|
||||||
|
void KeepAliveHandler::Handle(std::uint16_t a_PeerId, const protocol::data::KeepAlive& a_KeepAlive) {
|
||||||
|
AtomicEnttWorld world = m_World;
|
||||||
|
world->GetRegistry().view<KeepAliveSessionComponent>().each([a_PeerId, &a_KeepAlive](auto& keepAliveSession) {
|
||||||
|
if (keepAliveSession.m_PeerId == a_PeerId && keepAliveSession.m_LastKeepAliveId == a_KeepAlive.m_KeepAliveId) {
|
||||||
|
keepAliveSession.m_LastTime = Nz::GetElapsedMilliseconds();
|
||||||
|
keepAliveSession.m_RecievedResponse = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace server
|
||||||
|
} // namespace blitz
|
||||||
72
src/server/handlers/PlayerLoginHandler.cpp
Normal file
72
src/server/handlers/PlayerLoginHandler.cpp
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
#include <server/handlers/PlayerLoginHandler.h>
|
||||||
|
|
||||||
|
#include <Nazara/Core/Time.hpp>
|
||||||
|
#include <blitz/common/Format.h>
|
||||||
|
#include <blitz/common/Log.h>
|
||||||
|
#include <blitz/components/PlayerInfo.h>
|
||||||
|
#include <server/components/EnetConnection.h>
|
||||||
|
#include <server/components/KeepAliveSession.h>
|
||||||
|
#include <server/components/ServerIdCounter.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
PlayerLoginHandler::PlayerLoginHandler(network::EnetConnection& a_Connection, EnttWorld& a_World) :
|
||||||
|
protocol::PacketHandler(a_Connection, a_World) {
|
||||||
|
m_Slot.Connect(a_Connection.OnPlayerLogin,
|
||||||
|
[this](const protocol::data::PlayerLogin& a_PlayerLogin) { Handle(m_Connection.GetPeerId(), a_PlayerLogin); });
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerLoginHandler::~PlayerLoginHandler() {}
|
||||||
|
|
||||||
|
void PlayerLoginHandler::Handle(std::uint16_t a_PeerId, const protocol::data::PlayerLogin& a_PlayerLogin) {
|
||||||
|
AtomicEnttWorld world = m_World;
|
||||||
|
|
||||||
|
std::vector<PlayerInfoComponent> players;
|
||||||
|
|
||||||
|
for (auto [entity, playerInfo] : world->GetRegistry().view<PlayerInfoComponent>().each()) {
|
||||||
|
players.push_back(playerInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(world->GetRegistry().view<ServerIdCounterComponent>().size() == 1 && "There should be only one counter !");
|
||||||
|
|
||||||
|
auto& serverCounter =
|
||||||
|
world->GetRegistry().get<ServerIdCounterComponent>(world->GetRegistry().view<ServerIdCounterComponent>().front());
|
||||||
|
|
||||||
|
EntityID newEntityId = serverCounter.m_NextEntityId++;
|
||||||
|
|
||||||
|
PlayerInfoComponent newPlayer;
|
||||||
|
|
||||||
|
for (auto [entity, connection] : world->GetRegistry().view<EnetConnectionComponent>().each()) {
|
||||||
|
if (connection.m_Connection->GetPeerId() == m_Connection.GetPeerId()) {
|
||||||
|
// players should not try to log in twice
|
||||||
|
auto previousPlayerInfo = world->GetRegistry().try_get<PlayerInfoComponent>(entity);
|
||||||
|
if (previousPlayerInfo)
|
||||||
|
return;
|
||||||
|
|
||||||
|
newPlayer = world->GetRegistry().emplace<PlayerInfoComponent>(entity, newEntityId, a_PlayerLogin.m_PlayerName);
|
||||||
|
|
||||||
|
world->GetRegistry().emplace<KeepAliveSessionComponent>(
|
||||||
|
entity, m_Connection.GetPeerId(), 69, Nz::GetElapsedMilliseconds(), true);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// send logging success
|
||||||
|
m_Connection.SendLoggingSuccess({newEntityId});
|
||||||
|
|
||||||
|
// send player list
|
||||||
|
if (!players.empty())
|
||||||
|
m_Connection.SendPlayerList({players});
|
||||||
|
|
||||||
|
// broadcast player join
|
||||||
|
for (auto [entity, connection] : world->GetRegistry().view<EnetConnectionComponent>().each()) {
|
||||||
|
connection.m_Connection->SendPlayerJoin({newPlayer});
|
||||||
|
}
|
||||||
|
|
||||||
|
LogD(Format("[Server] %s joined !", a_PlayerLogin.m_PlayerName.c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace server
|
||||||
|
} // namespace blitz
|
||||||
48
src/server/systems/DisconnectSystem.cpp
Normal file
48
src/server/systems/DisconnectSystem.cpp
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#include <server/systems/DisconnectSystem.h>
|
||||||
|
|
||||||
|
#include <server/components/Disconnect.h>
|
||||||
|
#include <server/components/EnetConnection.h>
|
||||||
|
#include <blitz/components/PlayerRemove.h>
|
||||||
|
|
||||||
|
#include <server/Server.h>
|
||||||
|
|
||||||
|
#include <blitz/common/Format.h>
|
||||||
|
#include <blitz/common/Log.h>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
DisconnectSystem::DisconnectSystem(entt::registry&, EnttWorld& a_World, Server& a_Server) : m_World(a_World), m_Server(a_Server) {}
|
||||||
|
|
||||||
|
void DisconnectSystem::Update(Nz::Time elapsedTime) {
|
||||||
|
AtomicEnttWorld world = m_World;
|
||||||
|
|
||||||
|
entt::registry& registry = world->GetRegistry();
|
||||||
|
|
||||||
|
auto disconnects = registry.view<DisconnectComponent>();
|
||||||
|
|
||||||
|
// broadcast player leave
|
||||||
|
for (auto entity : disconnects) {
|
||||||
|
auto* player = registry.try_get<PlayerInfoComponent>(entity);
|
||||||
|
if (player) {
|
||||||
|
for (auto [entity, connection] : registry.view<EnetConnectionComponent>().each()) {
|
||||||
|
connection.m_Connection->SendPlayerLeave({player->m_PlayerId});
|
||||||
|
}
|
||||||
|
LogD(Format("[Server] %s left !", player->m_Pseudo.c_str()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// close connections
|
||||||
|
registry.view<EnetConnectionComponent, DisconnectComponent>().each(
|
||||||
|
[this](auto entity, EnetConnectionComponent& connection, DisconnectComponent disconnect) {
|
||||||
|
m_Server.CloseConnection(connection.m_Connection->GetPeerId());
|
||||||
|
});
|
||||||
|
|
||||||
|
// remove the entities
|
||||||
|
for (auto entity : disconnects) {
|
||||||
|
registry.emplace<PlayerRemoveComponent>(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace server
|
||||||
|
} // namespace blitz
|
||||||
42
src/server/systems/KeepAliveSystem.cpp
Normal file
42
src/server/systems/KeepAliveSystem.cpp
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#include <server/systems/KeepAliveSystem.h>
|
||||||
|
|
||||||
|
#include <server/components/Disconnect.h>
|
||||||
|
#include <server/components/EnetConnection.h>
|
||||||
|
#include <server/components/KeepAliveSession.h>
|
||||||
|
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
namespace blitz {
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
KeepAliveSystem::KeepAliveSystem(entt::registry&, EnttWorld& a_World) : m_World(a_World) {}
|
||||||
|
|
||||||
|
void KeepAliveSystem::Update(Nz::Time elapsedTime) {
|
||||||
|
AtomicEnttWorld world = m_World;
|
||||||
|
|
||||||
|
entt::registry& registry = world->GetRegistry();
|
||||||
|
|
||||||
|
registry.view<KeepAliveSessionComponent, EnetConnectionComponent>().each(
|
||||||
|
[®istry](auto entity, auto& keepAliveSession, auto& connection) {
|
||||||
|
auto duration = Nz::GetElapsedMilliseconds() - keepAliveSession.m_LastTime;
|
||||||
|
if (duration > Nz::Time::Seconds(10)) {
|
||||||
|
if (keepAliveSession.m_RecievedResponse) {
|
||||||
|
keepAliveSession.m_RecievedResponse = false;
|
||||||
|
keepAliveSession.m_LastTime = Nz::GetElapsedMilliseconds();
|
||||||
|
|
||||||
|
std::random_device rd;
|
||||||
|
std::uniform_int_distribution<std::uint64_t> dis(0, std::numeric_limits<std::uint64_t>::max());
|
||||||
|
std::uint64_t keepAliveId = dis(rd);
|
||||||
|
|
||||||
|
keepAliveSession.m_LastKeepAliveId = keepAliveId;
|
||||||
|
connection.m_Connection->SendKeepAlive({keepAliveId});
|
||||||
|
} else {
|
||||||
|
// We kick the player because he's not responding anymore
|
||||||
|
registry.emplace<DisconnectComponent>(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace server
|
||||||
|
} // namespace blitz
|
||||||
47
test/blitz/network/Enet_test.cpp
Normal file
47
test/blitz/network/Enet_test.cpp
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#include <blitz/network/EnetClient.h>
|
||||||
|
#include <blitz/network/EnetServer.h>
|
||||||
|
#include <blitz/utils/Test.h>
|
||||||
|
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
static constexpr std::uint16_t port = 25565;
|
||||||
|
|
||||||
|
int serverSendCount = 0;
|
||||||
|
int clientSendCount = 0;
|
||||||
|
|
||||||
|
blitz::network::EnetServer server(port);
|
||||||
|
blitz::network::EnetClient client(Nz::IpAddress{"127.0.0.1:" + std::to_string(port)});
|
||||||
|
blitz::network::EnetClient client1(Nz::IpAddress{"127.0.0.1:" + std::to_string(port)});
|
||||||
|
blitz::network::EnetClient client2(Nz::IpAddress{"127.0.0.1:" + std::to_string(port)});
|
||||||
|
blitz::network::EnetClient client3(Nz::IpAddress{"127.0.0.1:" + std::to_string(port)});
|
||||||
|
blitz::network::EnetClient client4(Nz::IpAddress{"127.0.0.1:" + std::to_string(port)});
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(1s);
|
||||||
|
|
||||||
|
blitz_test_assert(client.GetConnection().IsConnected());
|
||||||
|
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
blitz_test_assert(server.GetConnection(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
server.GetConnection(0)->OnKeepAlive.Connect([&server, &serverSendCount](const blitz::protocol::data::KeepAlive&) {
|
||||||
|
server.GetConnection(0)->SendKeepAlive({69});
|
||||||
|
serverSendCount++;
|
||||||
|
});
|
||||||
|
|
||||||
|
client.GetConnection().OnKeepAlive.Connect([&client, &clientSendCount](const blitz::protocol::data::KeepAlive&) {
|
||||||
|
client.GetConnection().SendKeepAlive({69});
|
||||||
|
clientSendCount++;
|
||||||
|
});
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(1s);
|
||||||
|
|
||||||
|
client.GetConnection().SendKeepAlive({69});
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(1s);
|
||||||
|
|
||||||
|
blitz_test_assert(serverSendCount > 10 && clientSendCount > 10);
|
||||||
|
|
||||||
|
return BLITZ_TEST_SUCCESSFUL;
|
||||||
|
}
|
||||||
15
test/blitz/protocol/PacketFactory_test.cpp
Normal file
15
test/blitz/protocol/PacketFactory_test.cpp
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#include <blitz/protocol/PacketFactory.h>
|
||||||
|
|
||||||
|
static int Test() {
|
||||||
|
for (std::size_t i = 0; i < static_cast<int>(blitz::protocol::PacketType::PACKET_COUNT); i++) {
|
||||||
|
blitz::protocol::PacketType packetType = blitz::protocol::PacketType(i);
|
||||||
|
|
||||||
|
if (blitz::protocol::PacketFactory::CreateReadOnlyPacket(packetType)->GetType() != packetType)
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
return Test();
|
||||||
|
}
|
||||||
31
test/blitz/protocol/PacketSerializer_test.cpp
Normal file
31
test/blitz/protocol/PacketSerializer_test.cpp
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#include <blitz/protocol/PacketSerializer.h>
|
||||||
|
|
||||||
|
#include <blitz/protocol/PacketFactory.h>
|
||||||
|
#include <blitz/utils/Test.h>
|
||||||
|
|
||||||
|
namespace bp = blitz::protocol;
|
||||||
|
|
||||||
|
static int TestAllPackets() {
|
||||||
|
for (std::size_t i = 0; i < static_cast<std::size_t>(bp::PacketType::PACKET_COUNT); i++) {
|
||||||
|
const auto& packet = bp::PacketFactory::CreateReadOnlyPacket(bp::PacketType(i));
|
||||||
|
|
||||||
|
Nz::ByteArray buffer = bp::PacketSerializer::Serialize(*packet.get());
|
||||||
|
|
||||||
|
bp::PacketPtr packet2 = bp::PacketSerializer::Deserialize(buffer);
|
||||||
|
|
||||||
|
blitz_test_assert(packet2 != nullptr);
|
||||||
|
|
||||||
|
blitz_test_assert(packet2->GetType() == packet->GetType());
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Test() {
|
||||||
|
bp::packets::ChatMessage packet({"caca"});
|
||||||
|
Nz::ByteArray buffer = bp::PacketSerializer::Serialize(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
Test();
|
||||||
|
return TestAllPackets();
|
||||||
|
}
|
||||||
47
xmake.lua
47
xmake.lua
@@ -1,16 +1,57 @@
|
|||||||
add_rules("mode.debug", "mode.release")
|
add_rules("mode.debug", "mode.release")
|
||||||
|
|
||||||
add_repositories("nazara-repo https://github.com/NazaraEngine/xmake-repo.git")
|
add_repositories("nazara-repo https://github.com/NazaraEngine/xmake-repo.git")
|
||||||
add_requires("nazaraengine", { debug = is_mode("debug") })
|
add_repositories("nazara-imgui-repo https://github.com/SweetId/NazaraImgui-xmake-repo")
|
||||||
|
|
||||||
|
add_requires("nazaraengine", { debug = false })
|
||||||
|
add_requires("nazaraimgui")
|
||||||
|
|
||||||
set_languages("c++20")
|
set_languages("c++20")
|
||||||
|
set_warnings("all")
|
||||||
|
|
||||||
|
if is_mode("release") then
|
||||||
|
set_warnings("all", "error")
|
||||||
|
end
|
||||||
|
|
||||||
|
add_includedirs("include")
|
||||||
|
|
||||||
target("Blitz2")
|
target("Blitz2")
|
||||||
|
set_kind("static")
|
||||||
|
add_files("src/blitz/**.cpp", "src/server/**.cpp")
|
||||||
|
add_packages("nazaraimgui", "nazaraengine", {public = true})
|
||||||
|
|
||||||
|
target("Blitz2Server")
|
||||||
set_kind("binary")
|
set_kind("binary")
|
||||||
add_files("src/*.cpp")
|
add_deps("Blitz2")
|
||||||
add_packages("nazaraengine")
|
set_default(false)
|
||||||
|
add_files("src/ServerMain.cpp")
|
||||||
|
|
||||||
|
|
||||||
|
target("Blitz2Client")
|
||||||
|
set_kind("binary")
|
||||||
|
add_files("src/ClientMain.cpp", "src/client/**.cpp")
|
||||||
|
add_deps("Blitz2")
|
||||||
set_rundir(".")
|
set_rundir(".")
|
||||||
|
|
||||||
|
|
||||||
|
-- Tests were introduced in that version
|
||||||
|
set_xmakever("2.8.5")
|
||||||
|
|
||||||
|
-- Tests
|
||||||
|
for _, file in ipairs(os.files("test/**.cpp")) do
|
||||||
|
local name = path.basename(file)
|
||||||
|
target(name)
|
||||||
|
set_kind("binary")
|
||||||
|
|
||||||
|
add_files(file)
|
||||||
|
|
||||||
|
set_default(false)
|
||||||
|
|
||||||
|
add_deps("Blitz2")
|
||||||
|
|
||||||
|
add_tests("compile_and_run")
|
||||||
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
-- If you want to known more usage about xmake, please see https://xmake.io
|
-- If you want to known more usage about xmake, please see https://xmake.io
|
||||||
--
|
--
|
||||||
|
|||||||
Reference in New Issue
Block a user