Compare commits
17 Commits
632a23c084
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 65a84ecab0 | |||
| e193d05417 | |||
| 82c7ff4895 | |||
| c9d94febbc | |||
| 15625e5c30 | |||
| 89a6219072 | |||
| 2df849b63e | |||
| e0d4dd053f | |||
| 7b693c7d43 | |||
| de6742f64d | |||
| adb6dce08a | |||
| af3ac8d37c | |||
| 282ddac80c | |||
| 2065ea8334 | |||
| b6530c1220 | |||
| c14234551d | |||
| 5d7add69aa |
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/compile_commands.json
|
||||
.cache
|
||||
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@@ -8,7 +8,7 @@
|
||||
"type": "xmake",
|
||||
"request": "launch",
|
||||
"name": "Debug",
|
||||
"target": "Blitz2",
|
||||
"target": "Blitz2Client",
|
||||
"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.
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
|
||||
@@ -26,7 +26,7 @@ class EnetClient : private NonCopyable {
|
||||
EnetConnection m_Connection;
|
||||
Nz::ENetHost m_Host;
|
||||
Nz::ENetPeer* m_Peer;
|
||||
std::thread m_Thread;
|
||||
std::jthread m_Thread;
|
||||
bool m_Running;
|
||||
|
||||
void Update();
|
||||
|
||||
@@ -17,6 +17,8 @@ class EnetServer : private NonCopyable {
|
||||
|
||||
void Destroy();
|
||||
|
||||
bool IsClosed() const;
|
||||
|
||||
void CloseConnection(std::uint16_t a_PeerId);
|
||||
|
||||
EnetConnection* GetConnection(std::uint16_t a_PeerId);
|
||||
@@ -33,7 +35,7 @@ class EnetServer : private NonCopyable {
|
||||
|
||||
Nz::ENetHost m_Host;
|
||||
bool m_Running;
|
||||
std::thread m_Thread;
|
||||
std::jthread m_Thread;
|
||||
std::map<std::uint16_t, std::unique_ptr<EnetConnection>> m_Connections;
|
||||
};
|
||||
|
||||
|
||||
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
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <Nazara/Core/EnttWorld.hpp>
|
||||
#include <atomic>
|
||||
#include <blitz/network/EnetClient.h>
|
||||
#include <blitz/protocol/PacketHandler.h>
|
||||
|
||||
@@ -18,6 +17,9 @@ class Client : private NonCopyable {
|
||||
|
||||
bool IsConnected();
|
||||
|
||||
NazaraSignal(OnClientReady);
|
||||
NazaraSignal(OnClientDisconnect);
|
||||
|
||||
private:
|
||||
EnttWorld m_World;
|
||||
std::unique_ptr<network::EnetClient> m_NetworkClient;
|
||||
|
||||
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
|
||||
@@ -10,6 +10,8 @@ class PlayerJoinHandler : public protocol::PacketHandler {
|
||||
|
||||
NazaraSlot(network::EnetConnection, OnPlayerJoin, m_Slot);
|
||||
|
||||
NazaraSignal(OnLocalPlayerReady);
|
||||
|
||||
private:
|
||||
void Handle(const protocol::data::PlayerJoin&);
|
||||
};
|
||||
|
||||
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
|
||||
@@ -17,6 +17,8 @@ class Server {
|
||||
|
||||
network::EnetConnection* GetConnection(std::uint16_t a_PeerId);
|
||||
|
||||
bool IsClosed() const;
|
||||
|
||||
void CloseConnection(std::uint16_t a_PeerId);
|
||||
|
||||
void CloseServer();
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <Nazara/Core/Time.hpp>
|
||||
#include <entt/entity/registry.hpp>
|
||||
#include <blitz/common/Types.h>
|
||||
|
||||
namespace blitz {
|
||||
@@ -9,9 +8,9 @@ namespace server {
|
||||
|
||||
class Server;
|
||||
|
||||
class DisconectSystem {
|
||||
class DisconnectSystem {
|
||||
public:
|
||||
DisconectSystem(entt::registry&, EnttWorld& a_World, Server& a_Server);
|
||||
DisconnectSystem(entt::registry&, EnttWorld& a_World, Server& a_Server);
|
||||
|
||||
void Update(Nz::Time elapsedTime);
|
||||
|
||||
|
||||
@@ -1,349 +1,20 @@
|
||||
#include <iostream>
|
||||
|
||||
#include <Nazara/Core.hpp>
|
||||
#include <Nazara/Graphics.hpp>
|
||||
#include <Nazara/Physics3D.hpp>
|
||||
#include <Nazara/Platform.hpp>
|
||||
#include <Nazara/Renderer.hpp>
|
||||
#include <Nazara/Widgets.hpp>
|
||||
#include <random>
|
||||
|
||||
constexpr Nz::UInt32 RenderMaskUI = 0x00010000;
|
||||
constexpr Nz::UInt32 RenderMask3D = 0x0000FFFF;
|
||||
|
||||
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>(RenderMask3D);
|
||||
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), RenderMask3D);
|
||||
|
||||
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>(deambModel, RenderMask3D);
|
||||
// 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 Nz::MillisecondClock updateClock;
|
||||
|
||||
static void CreateCamera(Nz::EnttWorld& world, Nz::Window& window, Nz::Application<Nz::Graphics, Nz::Physics3D, Nz::Widgets>& app,
|
||||
std::shared_ptr<Nz::RenderWindow>& renderTarget) {
|
||||
// Création de la caméra
|
||||
entt::handle cameraEntity = world.CreateEntity();
|
||||
|
||||
auto& cameraNode = cameraEntity.emplace<Nz::NodeComponent>();
|
||||
cameraNode.SetPosition({0, 2.5, 0});
|
||||
|
||||
auto& cameraComponent = cameraEntity.emplace<Nz::CameraComponent>(renderTarget, Nz::ProjectionType::Perspective);
|
||||
|
||||
cameraComponent.UpdateClearColor(Nz::Color(0.3f, 0.8f, 1.0f));
|
||||
cameraComponent.UpdateZNear(0.1f);
|
||||
cameraComponent.UpdateZFar(100.0f);
|
||||
cameraComponent.UpdateRenderMask(RenderMask3D);
|
||||
cameraComponent.UpdateRenderOrder(-1);
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
Nz::Vector3f playerSize{0.5, 2, 0.5};
|
||||
|
||||
{
|
||||
|
||||
std::shared_ptr<Nz::GraphicalMesh> boxMesh = Nz::GraphicalMesh::Build(Nz::Primitive::Box(playerSize));
|
||||
|
||||
std::shared_ptr<Nz::MaterialInstance> boxMaterial = Nz::MaterialInstance::Instantiate(Nz::MaterialType::Phong);
|
||||
boxMaterial->SetValueProperty("BaseColor", Nz::Color::sRGBToLinear({1.0, 0.0, 0.0}));
|
||||
|
||||
std::shared_ptr<Nz::Model> sphereModel = std::make_shared<Nz::Model>(boxMesh);
|
||||
sphereModel->SetMaterial(0, std::move(boxMaterial));
|
||||
playerEntity.emplace<Nz::GraphicsComponent>(std::move(sphereModel), RenderMask3D);
|
||||
}
|
||||
|
||||
std::shared_ptr<Nz::BoxCollider3D> boxCollider = std::make_shared<Nz::BoxCollider3D>(playerSize);
|
||||
|
||||
static const int playerMass = 100000;
|
||||
|
||||
Nz::RigidBody3D::DynamicSettings settings;
|
||||
settings.geom = boxCollider;
|
||||
settings.mass = playerMass;
|
||||
|
||||
playerEntity.emplace<Nz::RigidBody3DComponent>(settings);
|
||||
|
||||
app.AddUpdaterFunc([playerEntity]() {
|
||||
if (std::optional<Nz::Time> deltaTime = updateClock.RestartIfOver(Nz::Time::Milliseconds(30))) {
|
||||
Nz::Vector3f front = Get2DDirectionVectorFromRotation(camAngles.yaw.ToRadians());
|
||||
Nz::Vector3f left = Get2DDirectionVectorFromRotation(camAngles.yaw.ToRadians() + 3.1415926535 / 2.0);
|
||||
|
||||
auto& playerBody = playerEntity.get<Nz::RigidBody3DComponent>();
|
||||
|
||||
if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::Z)) {
|
||||
// std::cout << front << "\n";
|
||||
// std::cout << "Pos : " << playerBody.GetPosition() << " \n";
|
||||
playerBody.AddForce(front * 10.f * playerMass, Nz::CoordSys::Global);
|
||||
}
|
||||
|
||||
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::Global);
|
||||
}
|
||||
|
||||
if (Nz::Keyboard::IsKeyPressed(Nz::Keyboard::VKey::R)) {
|
||||
auto& playerNode = playerEntity.get<Nz::NodeComponent>();
|
||||
playerNode.SetPosition({0, 10, 0});
|
||||
playerBody.SetPosition({0, 10, 0});
|
||||
}
|
||||
|
||||
// playerBody.SetRotation({});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void SetupWidgets(Nz::EnttWorld& world, std::shared_ptr<Nz::RenderWindow> renderTarget, Nz::Window& window) {
|
||||
entt::handle cameraEntity = world.CreateEntity();
|
||||
{
|
||||
cameraEntity.emplace<Nz::NodeComponent>();
|
||||
|
||||
auto& cameraComponent = cameraEntity.emplace<Nz::CameraComponent>(renderTarget, Nz::ProjectionType::Orthographic);
|
||||
|
||||
cameraComponent.UpdateClearColor(Nz::Color(0.3f, 0.8f, 1.0f, 0.0f));
|
||||
cameraComponent.UpdateRenderMask(RenderMaskUI);
|
||||
cameraComponent.UpdateRenderOrder(1);
|
||||
// cameraComponent.UpdateSize({100, 100});
|
||||
}
|
||||
|
||||
Nz::Canvas canvas(world.GetRegistry(), window.GetEventHandler(), window.GetCursorController().CreateHandle(), RenderMaskUI);
|
||||
canvas.Resize({static_cast<float>(window.GetSize().x), static_cast<float>(window.GetSize().y)});
|
||||
canvas.EnableBackground(false);
|
||||
auto widget = canvas.Add<Nz::RichTextAreaWidget>();
|
||||
widget->EnableBackground(false);
|
||||
widget->EnableLineWrap(true);
|
||||
widget->SetBackgroundColor(Nz::Color(0, 0, 0, 50));
|
||||
widget->SetCharacterSize(22);
|
||||
widget->SetTextColor(Nz::Color::White());
|
||||
widget->SetTextOutlineThickness(1.f);
|
||||
widget->SetReadOnly(true);
|
||||
|
||||
auto m_chatEnteringBox = canvas.Add<Nz::TextAreaWidget>();
|
||||
m_chatEnteringBox->EnableBackground(false);
|
||||
m_chatEnteringBox->SetBackgroundColor(Nz::Color(255, 255, 255, 150));
|
||||
m_chatEnteringBox->SetTextColor(Nz::Color::Black());
|
||||
m_chatEnteringBox->SetText("ceci est un test incroyable");
|
||||
// m_chatEnteringBox->SetMaximumWidth(m_chatEnteringBox->GetPreferredWidth());
|
||||
|
||||
auto versionLabel = canvas.Add<Nz::LabelWidget>();
|
||||
versionLabel->UpdateText(
|
||||
Nz::SimpleTextDrawer::Draw(std::to_string(100000000) + "." + std::to_string(0) + "." + std::to_string(0), 14));
|
||||
versionLabel->Resize({500, 500});
|
||||
versionLabel->SetPosition({1000, 800});
|
||||
versionLabel->SetPosition({canvas.GetWidth() - versionLabel->GetWidth(), canvas.GetHeight() - versionLabel->GetHeight()});
|
||||
|
||||
|
||||
// Création d'un texte
|
||||
Nz::SimpleTextDrawer textDrawer;
|
||||
textDrawer.SetText("Hello world !");
|
||||
textDrawer.SetCharacterSize(72);
|
||||
textDrawer.SetTextOutlineThickness(4.f);
|
||||
|
||||
std::shared_ptr<Nz::TextSprite> textSprite = std::make_shared<Nz::TextSprite>();
|
||||
textSprite->Update(textDrawer);
|
||||
|
||||
entt::handle textEntity = world.CreateEntity();
|
||||
{
|
||||
auto& nodeComponent = textEntity.emplace<Nz::NodeComponent>();
|
||||
|
||||
auto& gfxComponent = textEntity.emplace<Nz::GraphicsComponent>(textSprite, RenderMaskUI);
|
||||
|
||||
Nz::Boxf textBox = textSprite->GetAABB();
|
||||
Nz::Vector2ui windowSize = window.GetSize();
|
||||
nodeComponent.SetPosition({windowSize.x / 2 - textBox.width / 2, windowSize.y / 2 - textBox.height / 2});
|
||||
}
|
||||
}
|
||||
|
||||
int Video(int argc, char** argv) {
|
||||
Nz::Application<Nz::Graphics, Nz::Physics3D, Nz::Widgets> 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::Milliseconds(20));
|
||||
physSystem.GetPhysWorld().SetGravity(Nz::Vector3f::Down() * 9.81f);
|
||||
|
||||
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);
|
||||
|
||||
auto renderTarget = std::make_shared<Nz::RenderWindow>(windowSwapchain);
|
||||
|
||||
CreateCamera(world, window, app, renderTarget);
|
||||
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;
|
||||
}
|
||||
});
|
||||
#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();
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
return Video(argc, argv);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#include <iostream>
|
||||
|
||||
void ConsoleLoop(Nz::Application<Nz::Network>& app, blitz::server::Server& server) {
|
||||
void ConsoleLoop(Nz::ApplicationBase& app, blitz::server::Server& server) {
|
||||
std::string line;
|
||||
while (true) {
|
||||
getline(std::cin, line);
|
||||
|
||||
@@ -4,18 +4,18 @@ namespace blitz {
|
||||
namespace network {
|
||||
|
||||
EnetClient::EnetClient(const Nz::IpAddress& address) : m_Running(true) {
|
||||
m_Host.Create(Nz::IpAddress::LoopbackIpV4, 1);
|
||||
m_Host.Create(address.GetProtocol() == Nz::NetProtocol::IPv4 ? Nz::IpAddress::LoopbackIpV4 : Nz::IpAddress::LoopbackIpV6, 1);
|
||||
m_Peer = m_Host.Connect(address);
|
||||
m_Thread = std::thread(&EnetClient::WorkerThread, this);
|
||||
m_Thread = std::jthread(&EnetClient::WorkerThread, this);
|
||||
m_Connection.SetPeer(m_Peer);
|
||||
}
|
||||
|
||||
EnetClient::~EnetClient() {
|
||||
if (m_Peer->IsConnected())
|
||||
Disconnect();
|
||||
m_Host.Destroy();
|
||||
m_Running = false;
|
||||
m_Thread.join();
|
||||
m_Thread.request_stop();
|
||||
m_Host.Destroy();
|
||||
}
|
||||
|
||||
void EnetClient::Disconnect() {
|
||||
|
||||
@@ -5,10 +5,12 @@
|
||||
namespace blitz {
|
||||
namespace network {
|
||||
|
||||
EnetServer::EnetServer(std::uint16_t port) : m_Running(true) {
|
||||
m_Host.Create(Nz::NetProtocol::Any, port, 80);
|
||||
m_Host.AllowsIncomingConnections(true);
|
||||
m_Thread = std::thread(&EnetServer::WorkerThread, this);
|
||||
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() {
|
||||
@@ -67,6 +69,8 @@ void EnetServer::Update() {
|
||||
break;
|
||||
}
|
||||
} while (m_Host.CheckEvents(&event));
|
||||
} else if (service < 0) {
|
||||
m_Running = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,9 +100,13 @@ void EnetServer::RemoveConnection(std::uint16_t a_PeerId) {
|
||||
|
||||
void EnetServer::Destroy() {
|
||||
m_Running = false;
|
||||
m_Thread.join();
|
||||
m_Thread.request_stop();
|
||||
m_Host.Destroy();
|
||||
}
|
||||
|
||||
bool EnetServer::IsClosed() const {
|
||||
return !m_Running;
|
||||
}
|
||||
|
||||
} // namespace network
|
||||
} // 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
|
||||
@@ -1,28 +1,38 @@
|
||||
#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>
|
||||
|
||||
#define RegisterHandler(Handler) m_Handlers.push_back(std::make_unique<Handler>(m_NetworkClient->GetConnection(), m_World))
|
||||
|
||||
namespace blitz {
|
||||
namespace client {
|
||||
|
||||
Client::Client(Nz::EnttWorld& a_World) : m_World({a_World}) {}
|
||||
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() {
|
||||
RegisterHandler(KeepAliveHandler);
|
||||
RegisterHandler(LoggingSuccessHandler);
|
||||
RegisterHandler(PlayerJoinHandler);
|
||||
RegisterHandler(PlayerLeaveHandler);
|
||||
RegisterHandler(PlayerListHandler);
|
||||
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() {
|
||||
@@ -38,6 +48,7 @@ void Client::Connect(const Nz::IpAddress& a_Ip) {
|
||||
void Client::Disconnect() {
|
||||
if (!m_NetworkClient)
|
||||
return;
|
||||
m_NetworkClient->GetConnection().SendDisconnect({});
|
||||
m_NetworkClient->Disconnect();
|
||||
UnbindHandlers();
|
||||
m_NetworkClient.reset(nullptr);
|
||||
@@ -51,6 +62,7 @@ bool Client::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)});
|
||||
|
||||
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
|
||||
@@ -27,6 +27,7 @@ void PlayerJoinHandler::Handle(const protocol::data::PlayerJoin& a_PlayerJoin) {
|
||||
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() {}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include <client/handlers/PlayerLeaveHandler.h>
|
||||
|
||||
#include <blitz/components/PlayerRemove.h>
|
||||
|
||||
namespace blitz {
|
||||
namespace client {
|
||||
|
||||
@@ -11,16 +13,12 @@ PlayerLeaveHandler::PlayerLeaveHandler(network::EnetConnection& a_Connection, En
|
||||
void PlayerLeaveHandler::Handle(const protocol::data::PlayerLeave& a_PlayerLeave) {
|
||||
AtomicEnttWorld world = m_World;
|
||||
|
||||
entt::entity playerLeft;
|
||||
|
||||
for (auto [player, playerInfo] : world->GetRegistry().view<PlayerInfoComponent>().each()) {
|
||||
if (playerInfo.m_PlayerId == a_PlayerLeave.m_PlayerId) {
|
||||
playerLeft = player;
|
||||
world->GetRegistry().emplace<PlayerRemoveComponent>(player);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
world->GetRegistry().destroy(playerLeft);
|
||||
}
|
||||
|
||||
PlayerLeaveHandler::~PlayerLeaveHandler() {}
|
||||
|
||||
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
|
||||
@@ -6,14 +6,13 @@
|
||||
#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 {
|
||||
|
||||
#define RegisterHandler(Handler) session.m_Handlers.push_back(std::make_unique<Handler>(*session.m_Connection, m_World))
|
||||
|
||||
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);
|
||||
@@ -21,7 +20,14 @@ Server::Server(std::uint16_t a_Port, Nz::EnttWorld& a_World) : m_World({a_World}
|
||||
m_NetworkServer.OnClientDisconnectTimeout.Connect(this, &Server::HandleDisconnect);
|
||||
}
|
||||
|
||||
Server::~Server() {}
|
||||
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;
|
||||
@@ -49,15 +55,16 @@ void Server::CreateEntity(network::EnetConnection& a_Connection) {
|
||||
void Server::RegisterSystems() {
|
||||
AtomicEnttWorld world = m_World;
|
||||
world->AddSystem<KeepAliveSystem>(m_World);
|
||||
world->AddSystem<DisconectSystem>(m_World, *this);
|
||||
world->AddSystem<DisconnectSystem>(m_World, *this);
|
||||
world->AddSystem<RemovePlayersSystem>(m_World);
|
||||
|
||||
auto counter = world->CreateEntity();
|
||||
counter.emplace<ServerIdCounterComponent>(0);
|
||||
}
|
||||
|
||||
void Server::RegisterHandlers(Session& session) {
|
||||
RegisterHandler(KeepAliveHandler);
|
||||
RegisterHandler(PlayerLoginHandler);
|
||||
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) {
|
||||
@@ -79,5 +86,9 @@ void Server::CloseServer() {
|
||||
}
|
||||
}
|
||||
|
||||
bool Server::IsClosed() const {
|
||||
return m_NetworkServer.IsClosed();
|
||||
}
|
||||
|
||||
} // namespace server
|
||||
} // namespace blitz
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <server/components/Disconnect.h>
|
||||
#include <server/components/EnetConnection.h>
|
||||
#include <blitz/components/PlayerRemove.h>
|
||||
|
||||
#include <server/Server.h>
|
||||
|
||||
@@ -11,9 +12,9 @@
|
||||
namespace blitz {
|
||||
namespace server {
|
||||
|
||||
DisconectSystem::DisconectSystem(entt::registry&, EnttWorld& a_World, Server& a_Server) : m_World(a_World), m_Server(a_Server) {}
|
||||
DisconnectSystem::DisconnectSystem(entt::registry&, EnttWorld& a_World, Server& a_Server) : m_World(a_World), m_Server(a_Server) {}
|
||||
|
||||
void DisconectSystem::Update(Nz::Time elapsedTime) {
|
||||
void DisconnectSystem::Update(Nz::Time elapsedTime) {
|
||||
AtomicEnttWorld world = m_World;
|
||||
|
||||
entt::registry& registry = world->GetRegistry();
|
||||
@@ -38,7 +39,9 @@ void DisconectSystem::Update(Nz::Time elapsedTime) {
|
||||
});
|
||||
|
||||
// remove the entities
|
||||
registry.destroy(disconnects.begin(), disconnects.end());
|
||||
for (auto entity : disconnects) {
|
||||
registry.emplace<PlayerRemoveComponent>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace server
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
add_rules("mode.debug", "mode.release")
|
||||
|
||||
add_repositories("nazara-repo https://github.com/NazaraEngine/xmake-repo.git")
|
||||
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_warnings("all")
|
||||
@@ -15,7 +18,7 @@ add_includedirs("include")
|
||||
target("Blitz2")
|
||||
set_kind("static")
|
||||
add_files("src/blitz/**.cpp", "src/server/**.cpp")
|
||||
add_packages("nazaraengine", {public = true})
|
||||
add_packages("nazaraimgui", "nazaraengine", {public = true})
|
||||
|
||||
target("Blitz2Server")
|
||||
set_kind("binary")
|
||||
|
||||
Reference in New Issue
Block a user