Compare commits

...

12 Commits

Author SHA1 Message Date
65a84ecab0 wrong directory name
Some checks failed
Linux arm64 / Build (push) Failing after 1h55m3s
2024-08-08 13:06:43 +02:00
e193d05417 Update action file 2024-08-08 13:05:31 +02:00
82c7ff4895 Add action file 2024-08-08 13:04:38 +02:00
c9d94febbc add passlist for imgui 2024-08-07 19:00:40 +02:00
15625e5c30 add imgui dep 2024-08-07 19:00:02 +02:00
89a6219072 add doom font 2024-08-07 15:17:41 +02:00
2df849b63e add imgui support 2024-08-07 15:01:42 +02:00
e0d4dd053f correct launch.json 2024-08-07 14:57:16 +02:00
7b693c7d43 add RemovePlayersSystem in client 2024-08-07 11:58:54 +02:00
de6742f64d working main menu 2024-08-07 11:54:09 +02:00
adb6dce08a use jthread 2024-08-07 11:50:07 +02:00
af3ac8d37c add cache to gitignore 2024-08-07 11:49:01 +02:00
35 changed files with 731 additions and 36 deletions

View 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
View File

@@ -9,3 +9,4 @@ NazaraLog.log
.vscode/settings.json
.vscode/compile_commands.json
.cache

2
.vscode/launch.json vendored
View File

@@ -8,7 +8,7 @@
"type": "xmake",
"request": "launch",
"name": "Debug",
"target": "Blitz2",
"target": "Blitz2Client",
"cwd": "${workspaceFolder}",
}
]

58
assets/example.passlist Normal file
View 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

Binary file not shown.

View File

@@ -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();

View File

@@ -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;
};

View File

@@ -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;

View File

@@ -22,6 +22,10 @@ class ClientApp : public Nz::ApplicationComponent {
void Update(Nz::Time elapsedTime) override;
Nz::Window* GetWindow() {
return m_Window;
}
private:
Nz::Window* m_Window;
std::unique_ptr<Nz::StateMachine> m_StateMachine;

View 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

View File

@@ -10,6 +10,8 @@ class PlayerJoinHandler : public protocol::PacketHandler {
NazaraSlot(network::EnetConnection, OnPlayerJoin, m_Slot);
NazaraSignal(OnLocalPlayerReady);
private:
void Handle(const protocol::data::PlayerJoin&);
};

View 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

View File

@@ -1,5 +1,6 @@
#pragma once
#include <Nazara/Widgets/TextAreaWidget.hpp>
#include <Nazara/Widgets/ButtonWidget.hpp>
#include <client/states/AbstractState.h>
@@ -13,6 +14,7 @@ class CreateServerState : public AbstractState {
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;

View 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

View 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

View File

@@ -1,6 +1,7 @@
#pragma once
#include <Nazara/Widgets/ButtonWidget.hpp>
#include <Nazara/Widgets/TextAreaWidget.hpp>
#include <client/states/AbstractState.h>
namespace blitz {
@@ -12,6 +13,7 @@ class JoinServerState : public AbstractState {
~JoinServerState();
private:
Nz::TextAreaWidget* m_InputAddress;
Nz::ButtonWidget* m_JoinServerButton;
Nz::ButtonWidget* m_BackButton;
std::shared_ptr<AbstractState> m_NextState;

View File

@@ -2,11 +2,12 @@
#include <Nazara/Widgets/ButtonWidget.hpp>
#include <client/states/AbstractState.h>
#include <client/states/ImGuiDrawer.h>
namespace blitz {
namespace client {
class MainMenuState : public AbstractState {
class MainMenuState : public AbstractState, private ImGuiDrawer {
public:
MainMenuState(std::shared_ptr<StateData>);
~MainMenuState();
@@ -21,6 +22,8 @@ class MainMenuState : public AbstractState {
void LayoutWidgets() override;
bool Update(Nz::StateMachine& fsm, Nz::Time elapsedTime) override;
void OnRenderImgui() override;
void OnJoinServerPressed();
void OnCreateServerPressed();
void OnOptionPressed();

View File

@@ -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();

View File

@@ -8,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);

View File

@@ -4,14 +4,17 @@
#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> app(argc, argv);
Nz::Application<Nz::Graphics, Nz::Physics3D, Nz::Widgets, Nz::TextRenderer, Nz::Imgui> app(argc, argv);
app.AddComponent<Nz::EntitySystemAppComponent>();
app.AddComponent<Nz::WindowingAppComponent>();
app.AddComponent<blitz::client::ClientApp>();
auto& client = app.AddComponent<blitz::client::ClientApp>();
app.AddComponent<blitz::client::ImGuiAppComponent>(*client.GetWindow());
return app.Run();
}

View File

@@ -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() {

View File

@@ -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

View File

@@ -1,5 +1,6 @@
#include <client/Client.h>
#include <blitz/systems/RemovePlayersSystem.h>
#include <client/handlers/KeepAliveHandler.h>
#include <client/handlers/LoggingSuccessHandler.h>
#include <client/handlers/PlayerJoinHandler.h>
@@ -10,17 +11,26 @@ namespace blitz {
namespace client {
Client::Client(Nz::EnttWorld& a_World) : m_World({a_World}) {
// TODO: bind RemovePlayersSystem
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));
m_Handlers.push_back(std::make_unique<PlayerJoinHandler>(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));
}
@@ -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)});

View File

@@ -7,6 +7,7 @@
#include <Nazara/Platform.hpp>
#include <Nazara/Renderer.hpp>
#include <Nazara/Widgets.hpp>
#include <NazaraImgui/NazaraImgui.hpp>
#include <client/states/MainMenuState.h>
#include <random>
@@ -36,11 +37,15 @@ ClientApp::ClientApp(Nz::ApplicationBase& app) : Nz::ApplicationComponent(app),
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, Nz::ProjectionType::Orthographic);
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);
@@ -52,6 +57,7 @@ ClientApp::ClientApp(Nz::ApplicationBase& app) : Nz::ApplicationComponent(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);

View 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

View File

@@ -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() {}

View 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

View File

@@ -1,7 +1,13 @@
#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 {
@@ -18,6 +24,18 @@ CreateServerState::CreateServerState(std::shared_ptr<StateData> a_StateData, std
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);
@@ -40,7 +58,7 @@ void CreateServerState::LayoutWidgets() {
constexpr float padding = 10.f;
std::array<Nz::BaseWidget*, 2> widgets = {m_CreateServerButton, m_BackButton};
std::array<Nz::BaseWidget*, 3> widgets = {m_InputPort, m_CreateServerButton, m_BackButton};
float maxWidth = 0.f;
float totalSize = padding * (widgets.size() - 1);
@@ -62,7 +80,24 @@ void CreateServerState::LayoutWidgets() {
}
}
void CreateServerState::OnCreateServerPressed() {}
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);

View 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

View 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

View File

@@ -1,3 +1,6 @@
#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>
@@ -12,6 +15,13 @@ JoinServerState::JoinServerState(std::shared_ptr<StateData> a_StateData, std::sh
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);
@@ -40,7 +50,7 @@ void JoinServerState::LayoutWidgets() {
constexpr float padding = 10.f;
std::array<Nz::BaseWidget*, 2> widgets = {m_JoinServerButton, m_BackButton};
std::array<Nz::BaseWidget*, 3> widgets = {m_InputAddress, m_JoinServerButton, m_BackButton};
float maxWidth = 0.f;
float totalSize = padding * (widgets.size() - 1);
@@ -62,7 +72,21 @@ void JoinServerState::LayoutWidgets() {
}
}
void JoinServerState::OnJoinServerPressed() {}
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);

View File

@@ -3,7 +3,8 @@
#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>
@@ -81,6 +82,12 @@ void MainMenuState::LayoutWidgets() {
}
}
void MainMenuState::OnRenderImgui() {
#ifndef NDEBUG
ImGui::ShowDemoWindow(nullptr);
#endif
}
void MainMenuState::OnJoinServerPressed() {
m_NextState = std::make_shared<JoinServerState>(GetStateDataPtr(), shared_from_this());
}

View File

@@ -13,8 +13,6 @@
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);
@@ -22,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;
@@ -50,7 +55,7 @@ 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();
@@ -58,8 +63,8 @@ void Server::RegisterSystems() {
}
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) {
@@ -81,5 +86,9 @@ void Server::CloseServer() {
}
}
bool Server::IsClosed() const {
return m_NetworkServer.IsClosed();
}
} // namespace server
} // namespace blitz

View File

@@ -12,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();

View File

@@ -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")