Merge branch 'main' into linux
This commit is contained in:
commit
d151d4bfaf
64 files changed
+679
-372
No files matched your search
+6
-1
@@ -1,9 +1,14 @@
|
|||||||
# how to build
|
# how to build
|
||||||
|
|
||||||
|
If emsdk is not installed, you need to clone it from here: https://github.com/emscripten-core/emsdk
|
||||||
|
|
||||||
Activate the environment:
|
and install:
|
||||||
```
|
```
|
||||||
C:\Work\Projects\emsdk\emsdk.bat install latest
|
C:\Work\Projects\emsdk\emsdk.bat install latest
|
||||||
|
```
|
||||||
|
|
||||||
|
Then activate the environment:
|
||||||
|
```
|
||||||
C:\Work\Projects\emsdk\emsdk.bat activate latest
|
C:\Work\Projects\emsdk\emsdk.bat activate latest
|
||||||
C:\Work\Projects\emsdk\emsdk_env.bat
|
C:\Work\Projects\emsdk\emsdk_env.bat
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -17,23 +17,10 @@
|
|||||||
width: 100vw; height: 100vh;
|
width: 100vw; height: 100vh;
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
/* Кнопка Fullscreen */
|
|
||||||
#fs-button {
|
|
||||||
position: absolute;
|
|
||||||
top: 10px; right: 10px;
|
|
||||||
padding: 10px;
|
|
||||||
z-index: 10;
|
|
||||||
background: rgba(255,255,255,0.3);
|
|
||||||
color: white; border: 1px solid white;
|
|
||||||
cursor: pointer;
|
|
||||||
font-family: sans-serif;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
#status { color: white; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }
|
#status { color: white; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<button id="fs-button">Fullscreen</button>
|
|
||||||
<div id="status">Downloading...</div>
|
<div id="status">Downloading...</div>
|
||||||
<canvas id="canvas" oncontextmenu="event.preventDefault()" tabindex="-1"></canvas>
|
<canvas id="canvas" oncontextmenu="event.preventDefault()" tabindex="-1"></canvas>
|
||||||
|
|
||||||
@@ -49,17 +36,6 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Кнопка Fullscreen
|
|
||||||
document.getElementById('fs-button').addEventListener('click', function() {
|
|
||||||
if (!document.fullscreenElement) {
|
|
||||||
document.documentElement.requestFullscreen().catch(e => {
|
|
||||||
console.error(`Error attempting to enable full-screen mode: ${e.message}`);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
document.exitFullscreen();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Обработка ориентации
|
// Обработка ориентации
|
||||||
window.addEventListener("orientationchange", function() {
|
window.addEventListener("orientationchange", function() {
|
||||||
// Chrome на Android обновляет innerWidth/Height не мгновенно.
|
// Chrome на Android обновляет innerWidth/Height не мгновенно.
|
||||||
|
|||||||
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
@@ -65,8 +65,8 @@
|
|||||||
"height": 56,
|
"height": 56,
|
||||||
"textures": {
|
"textures": {
|
||||||
"normal": "resources/game_over/Filledbuttons.png",
|
"normal": "resources/game_over/Filledbuttons.png",
|
||||||
"hover": "resources/game_over/Filledbuttons.png",
|
"hover": "resources/game_over/Variant5.png",
|
||||||
"pressed": "resources/game_over/Filledbuttons.png"
|
"pressed": "resources/game_over/Variant6.png"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -50,8 +50,8 @@
|
|||||||
"height": 56,
|
"height": 56,
|
||||||
"textures": {
|
"textures": {
|
||||||
"normal": "resources/main_menu/single.png",
|
"normal": "resources/main_menu/single.png",
|
||||||
"hover": "resources/main_menu/single.png",
|
"hover": "resources/main_menu/Variant5.png",
|
||||||
"pressed": "resources/main_menu/single.png"
|
"pressed": "resources/main_menu/Variant6.png"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -61,8 +61,8 @@
|
|||||||
"height": 56,
|
"height": 56,
|
||||||
"textures": {
|
"textures": {
|
||||||
"normal": "resources/main_menu/multi.png",
|
"normal": "resources/main_menu/multi.png",
|
||||||
"hover": "resources/main_menu/multi.png",
|
"hover": "resources/main_menu/Variant7.png",
|
||||||
"pressed": "resources/main_menu/multi.png"
|
"pressed": "resources/main_menu/Variant8.png"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
{
|
{
|
||||||
"emissionRate": 100.0,
|
"emissionRate": 10.0,
|
||||||
"maxParticles": 200,
|
"maxParticles": 200,
|
||||||
"particleSize": 0.3,
|
"particleSize": 0.3,
|
||||||
"biasX": 0.3,
|
"biasX": 0.3,
|
||||||
"emissionPoints": [
|
"emissionPoints": [
|
||||||
{
|
{
|
||||||
"position": [-1.3, 0, 0.0]
|
"position": [-1.0, 1.4, -3.5]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"position": [1.3, 0.0, 0.0]
|
"position": [1.0, 1.4, -3.5]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"speedRange": [0.5, 2.0],
|
"speedRange": [0.5, 2.0],
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"emissionRate": 10.0,
|
||||||
|
"maxParticles": 200,
|
||||||
|
"particleSize": 0.3,
|
||||||
|
"biasX": 0.3,
|
||||||
|
"emissionPoints": [
|
||||||
|
{
|
||||||
|
"position": [0.0, 2.8, -3.5]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"position": [0.0, 1.5, -3.5]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"speedRange": [0.5, 2.0],
|
||||||
|
"zSpeedRange": [1.0, 3.0],
|
||||||
|
"scaleRange": [0.8, 1.2],
|
||||||
|
"lifeTimeRange": [600.0, 1400.0],
|
||||||
|
"texture": "resources/spark.png",
|
||||||
|
"shaderProgramName": "spark"
|
||||||
|
}
|
||||||
@@ -2,14 +2,14 @@
|
|||||||
"emissionPoints": [
|
"emissionPoints": [
|
||||||
{ "position": [0.0, 0.0, 0.0] }
|
{ "position": [0.0, 0.0, 0.0] }
|
||||||
],
|
],
|
||||||
"texture": "resources/spark_white.png",
|
"texture": "resources/spark2.png",
|
||||||
"speedRange": [10.0, 30.0],
|
"speedRange": [5.0, 10.0],
|
||||||
"zSpeedRange": [-1.0, 1.0],
|
"zSpeedRange": [-1.0, 1.0],
|
||||||
"scaleRange": [0.5, 1.0],
|
"scaleRange": [0.5, 2.0],
|
||||||
"lifeTimeRange": [200.0, 800.0],
|
"lifeTimeRange": [200.0, 800.0],
|
||||||
"emissionRate": 50.0,
|
"emissionRate": 30.0,
|
||||||
"maxParticles": 10,
|
"maxParticles": 150,
|
||||||
"particleSize": 0.5,
|
"particleSize": 1.0,
|
||||||
"biasX": 0.1,
|
"biasX": 0.1,
|
||||||
"shaderProgramName": "default"
|
"shaderProgramName": "default"
|
||||||
}
|
}
|
||||||
+48
-16
@@ -6,6 +6,20 @@
|
|||||||
"width": "match_parent",
|
"width": "match_parent",
|
||||||
"height": "match_parent",
|
"height": "match_parent",
|
||||||
"children": [
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "TextView",
|
||||||
|
"name": "velocityText",
|
||||||
|
"x": 10,
|
||||||
|
"y": 10,
|
||||||
|
"width": 200,
|
||||||
|
"height": 40,
|
||||||
|
"horizontal_gravity": "left",
|
||||||
|
"vertical_gravity": "top",
|
||||||
|
"text": "Velocity: 0",
|
||||||
|
"fontSize": 24,
|
||||||
|
"color": [1.0, 1.0, 1.0, 1.0],
|
||||||
|
"centered": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "Button",
|
"type": "Button",
|
||||||
"name": "shootButton",
|
"name": "shootButton",
|
||||||
@@ -16,9 +30,10 @@
|
|||||||
"horizontal_gravity": "right",
|
"horizontal_gravity": "right",
|
||||||
"vertical_gravity": "bottom",
|
"vertical_gravity": "bottom",
|
||||||
"textures": {
|
"textures": {
|
||||||
"normal": "resources/shoot_normal.png",
|
"normal": "resources/fire.png",
|
||||||
"hover": "resources/shoot_hover.png",
|
"hover": "resources/fire2.png",
|
||||||
"pressed": "resources/shoot_pressed.png"
|
"pressed": "resources/fire2.png",
|
||||||
|
"disabled": "resources/fire_disabled.png"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -31,25 +46,42 @@
|
|||||||
"horizontal_gravity": "left",
|
"horizontal_gravity": "left",
|
||||||
"vertical_gravity": "bottom",
|
"vertical_gravity": "bottom",
|
||||||
"textures": {
|
"textures": {
|
||||||
"normal": "resources/shoot_normal.png",
|
"normal": "resources/fire.png",
|
||||||
"hover": "resources/shoot_hover.png",
|
"hover": "resources/fire2.png",
|
||||||
"pressed": "resources/shoot_pressed.png"
|
"pressed": "resources/fire2.png",
|
||||||
|
"disabled": "resources/fire_disabled.png"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "Slider",
|
"type": "Button",
|
||||||
"name": "velocitySlider",
|
"name": "minusButton",
|
||||||
"x": 10,
|
"x": -20,
|
||||||
"y": 200,
|
"y": 110,
|
||||||
"width": 80,
|
"width": 150,
|
||||||
"height": 300,
|
"height": 150,
|
||||||
"value": 0.0,
|
"border" : 20,
|
||||||
"orientation": "vertical",
|
|
||||||
"horizontal_gravity": "right",
|
"horizontal_gravity": "right",
|
||||||
"vertical_gravity": "bottom",
|
"vertical_gravity": "bottom",
|
||||||
"textures": {
|
"textures": {
|
||||||
"track": "resources/velocitySliderTexture.png",
|
"normal": "resources/button_minus.png",
|
||||||
"knob": "resources/velocitySliderButton.png"
|
"hover": "resources/button_minus_pressed.png",
|
||||||
|
"pressed": "resources/button_minus_pressed.png"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Button",
|
||||||
|
"name": "plusButton",
|
||||||
|
"x": -20,
|
||||||
|
"y": 220,
|
||||||
|
"width": 150,
|
||||||
|
"height": 150,
|
||||||
|
"border" : 20,
|
||||||
|
"horizontal_gravity": "right",
|
||||||
|
"vertical_gravity": "bottom",
|
||||||
|
"textures": {
|
||||||
|
"normal": "resources/button_plus.png",
|
||||||
|
"hover": "resources/button_plus_pressed.png",
|
||||||
|
"pressed": "resources/button_plus_pressed.png"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -26,6 +26,7 @@ add_subdirectory("${BOOST_SRC_DIR}/libs/predef" boost-predef-build EXCLUDE_FROM_
|
|||||||
|
|
||||||
# Исполняемый файл сервера
|
# Исполняемый файл сервера
|
||||||
add_executable(Server
|
add_executable(Server
|
||||||
|
server.h
|
||||||
server.cpp
|
server.cpp
|
||||||
../src/network/ClientState.h
|
../src/network/ClientState.h
|
||||||
../src/network/ClientState.cpp
|
../src/network/ClientState.cpp
|
||||||
|
|||||||
+146
-238
@@ -1,44 +1,11 @@
|
|||||||
#include <boost/beast/core.hpp>
|
#include "server.h"
|
||||||
#include <boost/beast/websocket.hpp>
|
#include <boost/beast/websocket.hpp>
|
||||||
#include <boost/asio/ip/tcp.hpp>
|
#include <boost/asio/ip/tcp.hpp>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
#include <mutex>
|
|
||||||
#include <map>
|
|
||||||
#include <queue>
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <Eigen/Dense>
|
|
||||||
#define _USE_MATH_DEFINES
|
|
||||||
#include <math.h>
|
|
||||||
#include "../src/network/ClientState.h"
|
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <unordered_set>
|
|
||||||
|
|
||||||
namespace beast = boost::beast;
|
|
||||||
namespace http = beast::http;
|
|
||||||
namespace websocket = beast::websocket;
|
|
||||||
namespace net = boost::asio;
|
|
||||||
using tcp = net::ip::tcp;
|
|
||||||
static constexpr float kWorldZOffset = 45000.0f;
|
|
||||||
static const Eigen::Vector3f kWorldOffset(0.0f, 0.0f, kWorldZOffset);
|
|
||||||
|
|
||||||
static constexpr float kShipRadius = 15.0f;
|
|
||||||
static constexpr float kSpawnShipMargin = 25.0f;
|
|
||||||
static constexpr float kSpawnBoxMargin = 15.0f;
|
|
||||||
static constexpr float kSpawnZJitter = 60.0f;
|
|
||||||
|
|
||||||
Eigen::Vector3f PickSafeSpawnPos(int forPlayerId);
|
|
||||||
|
|
||||||
struct DeathInfo {
|
|
||||||
int targetId = -1;
|
|
||||||
uint64_t serverTime = 0;
|
|
||||||
Eigen::Vector3f position = Eigen::Vector3f::Zero();
|
|
||||||
int killerId = -1;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<std::string> split(const std::string& s, char delimiter) {
|
std::vector<std::string> split(const std::string& s, char delimiter) {
|
||||||
std::vector<std::string> tokens;
|
std::vector<std::string> tokens;
|
||||||
@@ -50,75 +17,30 @@ std::vector<std::string> split(const std::string& s, char delimiter) {
|
|||||||
return tokens;
|
return tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ServerBox {
|
Session::Session(Server& server, tcp::socket&& socket, int id)
|
||||||
Eigen::Vector3f position;
|
: server_(server)
|
||||||
Eigen::Matrix3f rotation;
|
, ws_(std::move(socket))
|
||||||
float collisionRadius = 2.0f;
|
, id_(id)
|
||||||
bool destroyed = false;
|
, lastReceivedTime_(std::chrono::system_clock::now()) {
|
||||||
};
|
|
||||||
|
|
||||||
struct Projectile {
|
|
||||||
int shooterId = -1;
|
|
||||||
uint64_t spawnMs = 0;
|
|
||||||
Eigen::Vector3f pos;
|
|
||||||
Eigen::Vector3f vel;
|
|
||||||
float lifeMs = PROJECTILE_LIFE;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct BoxDestroyedInfo {
|
|
||||||
int boxIndex = -1;
|
|
||||||
uint64_t serverTime = 0;
|
|
||||||
Eigen::Vector3f position = Eigen::Vector3f::Zero();
|
|
||||||
int destroyedBy = -1;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<BoxDestroyedInfo> g_boxDestructions;
|
|
||||||
std::mutex g_boxDestructions_mutex;
|
|
||||||
|
|
||||||
std::vector<ServerBox> g_serverBoxes;
|
|
||||||
std::mutex g_boxes_mutex;
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<class Session>> g_sessions;
|
|
||||||
std::mutex g_sessions_mutex;
|
|
||||||
|
|
||||||
std::vector<Projectile> g_projectiles;
|
|
||||||
std::mutex g_projectiles_mutex;
|
|
||||||
|
|
||||||
std::unordered_set<int> g_dead_players;
|
|
||||||
std::mutex g_dead_mutex;
|
|
||||||
|
|
||||||
class Session;
|
|
||||||
|
|
||||||
void broadcastToAll(const std::string& message);
|
|
||||||
|
|
||||||
class Session : public std::enable_shared_from_this<Session> {
|
|
||||||
websocket::stream<beast::tcp_stream> ws_;
|
|
||||||
beast::flat_buffer buffer_;
|
|
||||||
int id_;
|
|
||||||
bool is_writing_ = false;
|
|
||||||
std::queue<std::shared_ptr<std::string>> writeQueue_;
|
|
||||||
std::mutex writeMutex_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
ClientStateInterval timedClientStates;
|
|
||||||
bool joined_ = false;
|
|
||||||
|
|
||||||
bool hasReservedSpawn_ = false;
|
|
||||||
Eigen::Vector3f reservedSpawn_ = Eigen::Vector3f(0.0f, 0.0f, kWorldZOffset);
|
|
||||||
|
|
||||||
std::string nickname = "Player";
|
|
||||||
int shipType = 0;
|
|
||||||
|
|
||||||
explicit Session(tcp::socket&& socket, int id)
|
|
||||||
: ws_(std::move(socket)), id_(id) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int get_id() const { return id_; }
|
bool Session::is_timed_out(std::chrono::system_clock::time_point now) const {
|
||||||
|
if (!joined_) return false;
|
||||||
|
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - lastReceivedTime_).count();
|
||||||
|
return elapsed > PLAYER_TIMEOUT_MS;
|
||||||
|
}
|
||||||
|
|
||||||
bool hasSpawnReserved() const { return hasReservedSpawn_; }
|
void Session::force_disconnect() {
|
||||||
const Eigen::Vector3f& reservedSpawn() const { return reservedSpawn_; }
|
ws_.async_close(websocket::close_code::normal,
|
||||||
|
[self = shared_from_this()](beast::error_code) {});
|
||||||
|
}
|
||||||
|
|
||||||
bool fetchStateAtTime(std::chrono::system_clock::time_point targetTime, ClientState& outState) const {
|
int Session::get_id() const { return id_; }
|
||||||
|
|
||||||
|
bool Session::hasSpawnReserved() const { return hasReservedSpawn_; }
|
||||||
|
const Eigen::Vector3f& Session::reservedSpawn() const { return reservedSpawn_; }
|
||||||
|
|
||||||
|
bool Session::fetchStateAtTime(std::chrono::system_clock::time_point targetTime, ClientState& outState) const {
|
||||||
if (timedClientStates.canFetchClientStateAtTime(targetTime)) {
|
if (timedClientStates.canFetchClientStateAtTime(targetTime)) {
|
||||||
outState = timedClientStates.fetchClientStateAtTime(targetTime);
|
outState = timedClientStates.fetchClientStateAtTime(targetTime);
|
||||||
return true;
|
return true;
|
||||||
@@ -126,7 +48,7 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_message(const std::string& msg) {
|
void Session::send_message(const std::string& msg) {
|
||||||
auto ss = std::make_shared<std::string>(msg);
|
auto ss = std::make_shared<std::string>(msg);
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(writeMutex_);
|
std::lock_guard<std::mutex> lock(writeMutex_);
|
||||||
@@ -135,10 +57,10 @@ public:
|
|||||||
doWrite();
|
doWrite();
|
||||||
}
|
}
|
||||||
|
|
||||||
void run() {
|
void Session::run() {
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(g_sessions_mutex);
|
std::lock_guard<std::mutex> lock(server_.g_sessions_mutex);
|
||||||
g_sessions.push_back(shared_from_this());
|
server_.g_sessions.push_back(shared_from_this());
|
||||||
}
|
}
|
||||||
|
|
||||||
ws_.async_accept([self = shared_from_this()](beast::error_code ec) {
|
ws_.async_accept([self = shared_from_this()](beast::error_code ec) {
|
||||||
@@ -148,7 +70,7 @@ public:
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsMessageValid(const std::string& fullMessage) {
|
bool Session::IsMessageValid(const std::string& fullMessage) {
|
||||||
#ifdef ENABLE_NETWORK_CHECKSUM
|
#ifdef ENABLE_NETWORK_CHECKSUM
|
||||||
size_t hashPos = fullMessage.find("#hash:");
|
size_t hashPos = fullMessage.find("#hash:");
|
||||||
if (hashPos == std::string::npos) {
|
if (hashPos == std::string::npos) {
|
||||||
@@ -170,16 +92,14 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
void Session::sendBoxesToClient() {
|
||||||
|
std::lock_guard<std::mutex> lock(server_.g_boxes_mutex);
|
||||||
void sendBoxesToClient() {
|
|
||||||
std::lock_guard<std::mutex> lock(g_boxes_mutex);
|
|
||||||
|
|
||||||
std::string boxMsg = "BOXES:";
|
std::string boxMsg = "BOXES:";
|
||||||
bool first = true;
|
bool first = true;
|
||||||
|
|
||||||
for (size_t i = 0; i < g_serverBoxes.size(); ++i) {
|
for (size_t i = 0; i < server_.g_serverBoxes.size(); ++i) {
|
||||||
const auto& box = g_serverBoxes[i];
|
const auto& box = server_.g_serverBoxes[i];
|
||||||
|
|
||||||
if (box.destroyed) continue;
|
if (box.destroyed) continue;
|
||||||
|
|
||||||
@@ -199,14 +119,11 @@ private:
|
|||||||
"0";
|
"0";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Если все коробки уничтожены — отправится просто "BOXES:" (это нормально)
|
|
||||||
send_message(boxMsg);
|
send_message(boxMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public:
|
void Session::init()
|
||||||
|
|
||||||
void init()
|
|
||||||
{
|
{
|
||||||
sendBoxesToClient();
|
sendBoxesToClient();
|
||||||
|
|
||||||
@@ -224,23 +141,17 @@ public:
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientState get_latest_state(std::chrono::system_clock::time_point now) {
|
ClientState Session::get_latest_state(std::chrono::system_clock::time_point now) {
|
||||||
if (timedClientStates.timedStates.empty()) {
|
if (timedClientStates.timedStates.empty()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Берем самое последнее известное состояние
|
|
||||||
ClientState latest = timedClientStates.timedStates.back();
|
ClientState latest = timedClientStates.timedStates.back();
|
||||||
|
|
||||||
// 3. Применяем компенсацию лага (экстраполяцию).
|
|
||||||
// Функция внутри использует simulate_physics, чтобы переместить объект
|
|
||||||
// из точки lastUpdateServerTime в точку now.
|
|
||||||
latest.apply_lag_compensation(now);
|
latest.apply_lag_compensation(now);
|
||||||
|
|
||||||
return latest;
|
return latest;
|
||||||
}
|
}
|
||||||
|
|
||||||
void doWrite() {
|
void Session::doWrite() {
|
||||||
std::lock_guard<std::mutex> lock(writeMutex_);
|
std::lock_guard<std::mutex> lock(writeMutex_);
|
||||||
if (is_writing_ || writeQueue_.empty()) {
|
if (is_writing_ || writeQueue_.empty()) {
|
||||||
return;
|
return;
|
||||||
@@ -263,17 +174,20 @@ public:
|
|||||||
self->doWrite();
|
self->doWrite();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
private:
|
|
||||||
|
|
||||||
void do_read() {
|
void Session::do_read() {
|
||||||
ws_.async_read(buffer_, [self = shared_from_this()](beast::error_code ec, std::size_t) {
|
ws_.async_read(buffer_, [self = shared_from_this()](beast::error_code ec, std::size_t) {
|
||||||
if (ec) {
|
if (ec) {
|
||||||
std::lock_guard<std::mutex> lock(g_sessions_mutex);
|
if (self->joined_) {
|
||||||
g_sessions.erase(std::remove_if(g_sessions.begin(), g_sessions.end(),
|
self->server_.broadcastToAllExceptId("PLAYER_LEFT:" + std::to_string(self->id_), self->id_);
|
||||||
|
std::cout << "Client " << self->id_ << " disconnected, broadcasting PLAYER_LEFT\n";
|
||||||
|
}
|
||||||
|
std::lock_guard<std::mutex> lock(self->server_.g_sessions_mutex);
|
||||||
|
self->server_.g_sessions.erase(std::remove_if(self->server_.g_sessions.begin(), self->server_.g_sessions.end(),
|
||||||
[self](const std::shared_ptr<Session>& session) {
|
[self](const std::shared_ptr<Session>& session) {
|
||||||
return session.get() == self.get();
|
return session.get() == self.get();
|
||||||
}), g_sessions.end());
|
}), self->server_.g_sessions.end());
|
||||||
std::cout << "Client " << self->id_ << " disconnected\n";
|
std::cout << "Client " << self->id_ << " removed from session list\n";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,11 +199,12 @@ private:
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void process_message(const std::string& msg) {
|
void Session::process_message(const std::string& msg) {
|
||||||
if (!IsMessageValid(msg)) {
|
if (!IsMessageValid(msg)) {
|
||||||
std::cout << "[Security] Invalid packet hash. Dropping message: " << msg << std::endl;
|
std::cout << "[Security] Invalid packet hash. Dropping message: " << msg << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
lastReceivedTime_ = std::chrono::system_clock::now();
|
||||||
std::string cleanMessage = msg.substr(0, msg.find("#hash:"));
|
std::string cleanMessage = msg.substr(0, msg.find("#hash:"));
|
||||||
|
|
||||||
std::cout << "Received from player " << id_ << ": " << cleanMessage << std::endl;
|
std::cout << "Received from player " << id_ << ": " << cleanMessage << std::endl;
|
||||||
@@ -316,7 +231,7 @@ private:
|
|||||||
uint64_t now_ms = static_cast<uint64_t>(
|
uint64_t now_ms = static_cast<uint64_t>(
|
||||||
std::chrono::duration_cast<std::chrono::milliseconds>(now_tp.time_since_epoch()).count());
|
std::chrono::duration_cast<std::chrono::milliseconds>(now_tp.time_since_epoch()).count());
|
||||||
|
|
||||||
Eigen::Vector3f spawnPos = PickSafeSpawnPos(id_);
|
Eigen::Vector3f spawnPos = server_.PickSafeSpawnPos(id_);
|
||||||
this->hasReservedSpawn_ = true;
|
this->hasReservedSpawn_ = true;
|
||||||
this->reservedSpawn_ = spawnPos;
|
this->reservedSpawn_ = spawnPos;
|
||||||
|
|
||||||
@@ -342,27 +257,16 @@ private:
|
|||||||
std::string eventMsg =
|
std::string eventMsg =
|
||||||
"EVENT:" + std::to_string(id_) + ":UPD:" + std::to_string(now_ms) + ":" + st.formPingMessageContent();
|
"EVENT:" + std::to_string(id_) + ":UPD:" + std::to_string(now_ms) + ":" + st.formPingMessageContent();
|
||||||
|
|
||||||
{
|
server_.broadcastToAllExceptId(eventMsg, id_);
|
||||||
std::lock_guard<std::mutex> lock(g_sessions_mutex);
|
|
||||||
for (auto& session : g_sessions) {
|
|
||||||
if (session->get_id() == id_) continue;
|
|
||||||
session->send_message(eventMsg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::cout << "Server: Player " << id_ << " joined as [" << nick << "] shipType=" << sType << std::endl;
|
std::cout << "Server: Player " << id_ << " joined as [" << nick << "] shipType=" << sType << std::endl;
|
||||||
|
|
||||||
std::string info = "PLAYERINFO:" + std::to_string(id_) + ":" + nick + ":" + std::to_string(sType);
|
std::string info = "PLAYERINFO:" + std::to_string(id_) + ":" + nick + ":" + std::to_string(sType);
|
||||||
|
server_.broadcastToAllExceptId(info, id_);
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(g_sessions_mutex);
|
std::lock_guard<std::mutex> lock(server_.g_sessions_mutex);
|
||||||
for (auto& session : g_sessions) {
|
for (auto& session : server_.g_sessions) {
|
||||||
if (session->get_id() == this->id_) continue;
|
|
||||||
session->send_message(info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(g_sessions_mutex);
|
|
||||||
for (auto& session : g_sessions) {
|
|
||||||
if (session->get_id() == this->id_) continue;
|
if (session->get_id() == this->id_) continue;
|
||||||
std::string otherInfo = "PLAYERINFO:" + std::to_string(session->get_id()) + ":" + session->nickname + ":" + std::to_string(session->shipType);
|
std::string otherInfo = "PLAYERINFO:" + std::to_string(session->get_id()) + ":" + session->nickname + ":" + std::to_string(session->shipType);
|
||||||
|
|
||||||
@@ -376,8 +280,8 @@ private:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> gd(g_dead_mutex);
|
std::lock_guard<std::mutex> gd(server_.g_dead_mutex);
|
||||||
if (g_dead_players.find(id_) != g_dead_players.end()) {
|
if (server_.g_dead_players.find(id_) != server_.g_dead_players.end()) {
|
||||||
std::cout << "Server: Ignoring UPD from dead player " << id_ << std::endl;
|
std::cout << "Server: Ignoring UPD from dead player " << id_ << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -399,8 +303,8 @@ private:
|
|||||||
}
|
}
|
||||||
else if (type == "RESPAWN") {
|
else if (type == "RESPAWN") {
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> gd(g_dead_mutex);
|
std::lock_guard<std::mutex> gd(server_.g_dead_mutex);
|
||||||
g_dead_players.erase(id_);
|
server_.g_dead_players.erase(id_);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -410,7 +314,7 @@ private:
|
|||||||
ClientState st;
|
ClientState st;
|
||||||
st.id = id_;
|
st.id = id_;
|
||||||
|
|
||||||
Eigen::Vector3f spawnPos = PickSafeSpawnPos(id_);
|
Eigen::Vector3f spawnPos = server_.PickSafeSpawnPos(id_);
|
||||||
st.position = spawnPos;
|
st.position = spawnPos;
|
||||||
|
|
||||||
this->hasReservedSpawn_ = true;
|
this->hasReservedSpawn_ = true;
|
||||||
@@ -431,13 +335,13 @@ private:
|
|||||||
"SPAWN:" + std::to_string(id_) + ":" + std::to_string(now_ms) + ":" + st.formPingMessageContent()
|
"SPAWN:" + std::to_string(id_) + ":" + std::to_string(now_ms) + ":" + st.formPingMessageContent()
|
||||||
);
|
);
|
||||||
std::string respawnMsg = "RESPAWN_ACK:" + std::to_string(id_);
|
std::string respawnMsg = "RESPAWN_ACK:" + std::to_string(id_);
|
||||||
broadcastToAll(respawnMsg);
|
server_.broadcastToAll(respawnMsg);
|
||||||
|
|
||||||
std::string playerInfo = "PLAYERINFO:" + std::to_string(id_) + ":" + st.nickname + ":" + std::to_string(st.shipType);
|
std::string playerInfo = "PLAYERINFO:" + std::to_string(id_) + ":" + st.nickname + ":" + std::to_string(st.shipType);
|
||||||
broadcastToAll(playerInfo);
|
server_.broadcastToAll(playerInfo);
|
||||||
|
|
||||||
std::string eventMsg = "EVENT:" + std::to_string(id_) + ":UPD:" + std::to_string(now_ms) + ":" + st.formPingMessageContent();
|
std::string eventMsg = "EVENT:" + std::to_string(id_) + ":UPD:" + std::to_string(now_ms) + ":" + st.formPingMessageContent();
|
||||||
broadcastToAll(eventMsg);
|
server_.broadcastToAll(eventMsg);
|
||||||
|
|
||||||
std::cout << "Server: Player " << id_ << " respawned, broadcasted RESPAWN_ACK, PLAYERINFO and initial UPD\n";
|
std::cout << "Server: Player " << id_ << " respawned, broadcasted RESPAWN_ACK, PLAYERINFO and initial UPD\n";
|
||||||
}
|
}
|
||||||
@@ -454,12 +358,6 @@ private:
|
|||||||
);
|
);
|
||||||
float velocity = std::stof(parts[9]);
|
float velocity = std::stof(parts[9]);
|
||||||
|
|
||||||
int shotCount = 2;
|
|
||||||
if (parts.size() >= 11) {
|
|
||||||
try { shotCount = std::stoi(parts[10]); }
|
|
||||||
catch (...) { shotCount = 2; }
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string broadcast = "PROJECTILE:" +
|
std::string broadcast = "PROJECTILE:" +
|
||||||
std::to_string(id_) + ":" +
|
std::to_string(id_) + ":" +
|
||||||
std::to_string(clientTime) + ":" +
|
std::to_string(clientTime) + ":" +
|
||||||
@@ -472,50 +370,14 @@ private:
|
|||||||
std::to_string(dir.z()) + ":" +
|
std::to_string(dir.z()) + ":" +
|
||||||
std::to_string(velocity);
|
std::to_string(velocity);
|
||||||
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(g_sessions_mutex);
|
server_.broadcastToAllExceptId(broadcast, id_);
|
||||||
for (auto& session : g_sessions) {
|
|
||||||
if (session->get_id() != id_) {
|
server_.createProjectile(id_, pos, dir, velocity);
|
||||||
session->send_message(broadcast);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
Eigen::Vector3f Server::PickSafeSpawnPos(int forPlayerId)
|
||||||
const std::vector<Eigen::Vector3f> localOffsets = {
|
|
||||||
Eigen::Vector3f(-1.5f, 0.9f - 6.f, 5.0f),
|
|
||||||
Eigen::Vector3f(1.5f, 0.9f - 6.f, 5.0f)
|
|
||||||
};
|
|
||||||
|
|
||||||
uint64_t now_ms = std::chrono::duration_cast<std::chrono::milliseconds>((
|
|
||||||
std::chrono::system_clock::now().time_since_epoch())).count();
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> pl(g_projectiles_mutex);
|
|
||||||
for (int i = 0; i < std::min(shotCount, (int)localOffsets.size()); ++i) {
|
|
||||||
Projectile pr;
|
|
||||||
pr.shooterId = id_;
|
|
||||||
pr.spawnMs = now_ms;
|
|
||||||
Eigen::Vector3f shotPos = pos + dir.toRotationMatrix() * localOffsets[i];
|
|
||||||
pr.pos = shotPos;
|
|
||||||
Eigen::Vector3f localForward(0.0f, 0.0f, -1.0f);
|
|
||||||
Eigen::Vector3f worldForward = dir.toRotationMatrix() * localForward;
|
|
||||||
float len = worldForward.norm();
|
|
||||||
if (len > 1e-6f) worldForward /= len;
|
|
||||||
pr.vel = worldForward * velocity;
|
|
||||||
pr.lifeMs = 15000.0f;
|
|
||||||
g_projectiles.push_back(pr);
|
|
||||||
|
|
||||||
std::cout << "Server: Created projectile from player " << id_
|
|
||||||
<< " at pos (" << shotPos.x() << ", " << shotPos.y() << ", " << shotPos.z()
|
|
||||||
<< ") vel (" << pr.vel.x() << ", " << pr.vel.y() << ", " << pr.vel.z() << ")" << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
Eigen::Vector3f PickSafeSpawnPos(int forPlayerId)
|
|
||||||
{
|
{
|
||||||
static thread_local std::mt19937 rng{ std::random_device{}() };
|
static thread_local std::mt19937 rng{ std::random_device{}() };
|
||||||
|
|
||||||
@@ -580,14 +442,54 @@ Eigen::Vector3f PickSafeSpawnPos(int forPlayerId)
|
|||||||
return Eigen::Vector3f(600.0f + a * 100.0f, -600.0f + b * 100.0f, kWorldZOffset);
|
return Eigen::Vector3f(600.0f + a * 100.0f, -600.0f + b * 100.0f, kWorldZOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
void broadcastToAll(const std::string& message) {
|
void Server::broadcastToAll(const std::string& message) {
|
||||||
std::lock_guard<std::mutex> lock(g_sessions_mutex);
|
std::lock_guard<std::mutex> lock(g_sessions_mutex);
|
||||||
for (const auto& session : g_sessions) {
|
for (const auto& session : g_sessions) {
|
||||||
session->send_message(message);
|
session->send_message(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_world(net::steady_timer& timer, net::io_context& ioc) {
|
void Server::broadcastToAllExceptId(const std::string& message, int id)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(g_sessions_mutex);
|
||||||
|
for (auto& session : g_sessions) {
|
||||||
|
if (session->get_id() == id) continue;
|
||||||
|
session->send_message(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::createProjectile(int id, Eigen::Vector3f pos, Eigen::Quaternionf dir, float velocity)
|
||||||
|
{
|
||||||
|
const std::vector<Eigen::Vector3f> localOffsets = {
|
||||||
|
Eigen::Vector3f(-1.5f, 0.9f - 6.f, 5.0f),
|
||||||
|
Eigen::Vector3f(1.5f, 0.9f - 6.f, 5.0f)
|
||||||
|
};
|
||||||
|
|
||||||
|
uint64_t now_ms = std::chrono::duration_cast<std::chrono::milliseconds>((
|
||||||
|
std::chrono::system_clock::now().time_since_epoch())).count();
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> pl(g_projectiles_mutex);
|
||||||
|
for (int i = 0; i < localOffsets.size(); ++i) {
|
||||||
|
Projectile pr;
|
||||||
|
pr.shooterId = id;
|
||||||
|
pr.spawnMs = now_ms;
|
||||||
|
Eigen::Vector3f shotPos = pos + dir.toRotationMatrix() * localOffsets[i];
|
||||||
|
pr.pos = shotPos;
|
||||||
|
Eigen::Vector3f localForward(0.0f, 0.0f, -1.0f);
|
||||||
|
Eigen::Vector3f worldForward = dir.toRotationMatrix() * localForward;
|
||||||
|
float len = worldForward.norm();
|
||||||
|
if (len > 1e-6f) worldForward /= len;
|
||||||
|
pr.vel = worldForward * velocity;
|
||||||
|
pr.lifeMs = 15000.0f;
|
||||||
|
g_projectiles.push_back(pr);
|
||||||
|
|
||||||
|
std::cout << "Server: Created projectile from player " << id
|
||||||
|
<< " at pos (" << shotPos.x() << ", " << shotPos.y() << ", " << shotPos.z()
|
||||||
|
<< ") vel (" << pr.vel.x() << ", " << pr.vel.y() << ", " << pr.vel.z() << ")" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::update_world() {
|
||||||
|
|
||||||
static auto last_snapshot_time = std::chrono::system_clock::now();
|
static auto last_snapshot_time = std::chrono::system_clock::now();
|
||||||
|
|
||||||
@@ -595,26 +497,24 @@ void update_world(net::steady_timer& timer, net::io_context& ioc) {
|
|||||||
uint64_t now_ms = static_cast<uint64_t>(
|
uint64_t now_ms = static_cast<uint64_t>(
|
||||||
std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count());
|
std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count());
|
||||||
|
|
||||||
// --- Snapshot every 500ms ---
|
// --- Detect and force-disconnect timed-out players ---
|
||||||
/*if (std::chrono::duration_cast<std::chrono::milliseconds>(now - last_snapshot_time).count() >= 500) {
|
{
|
||||||
last_snapshot_time = now;
|
std::vector<std::shared_ptr<Session>> timedOut;
|
||||||
|
{
|
||||||
std::string snapshot_msg = "SNAPSHOT:" + std::to_string(now_ms);
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(g_sessions_mutex);
|
std::lock_guard<std::mutex> lock(g_sessions_mutex);
|
||||||
for (auto& session : g_sessions) {
|
for (auto& session : g_sessions) {
|
||||||
ClientState st = session->get_latest_state(now);
|
if (session->is_timed_out(now)) {
|
||||||
snapshot_msg += "|" + std::to_string(session->get_id()) + ":" + st.formPingMessageContent();
|
timedOut.push_back(session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto& session : timedOut) {
|
||||||
|
std::cout << "Server: Player " << session->get_id()
|
||||||
|
<< " timed out after " << PLAYER_TIMEOUT_MS << "ms, forcing disconnect\n";
|
||||||
|
session->force_disconnect();
|
||||||
}
|
}
|
||||||
for (auto& session : g_sessions) {
|
|
||||||
session->send_message(snapshot_msg);
|
|
||||||
}
|
}
|
||||||
}*/
|
|
||||||
|
|
||||||
// --- Tick: broadcast each player's latest state to all others (20Hz) ---
|
|
||||||
// Send the raw last-known state with its original timestamp, NOT an extrapolated one.
|
|
||||||
// Extrapolating here causes snap-back: if a player stops rotating, the server would
|
|
||||||
// keep sending over-rotated positions until the new state arrives, then B snaps back.
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(g_sessions_mutex);
|
std::lock_guard<std::mutex> lock(g_sessions_mutex);
|
||||||
for (auto& sender : g_sessions) {
|
for (auto& sender : g_sessions) {
|
||||||
@@ -834,13 +734,13 @@ void update_world(net::steady_timer& timer, net::io_context& ioc) {
|
|||||||
|
|
||||||
// --- Schedule next tick in 50ms ---
|
// --- Schedule next tick in 50ms ---
|
||||||
timer.expires_after(std::chrono::milliseconds(50));
|
timer.expires_after(std::chrono::milliseconds(50));
|
||||||
timer.async_wait([&timer, &ioc](const boost::system::error_code& ec) {
|
timer.async_wait([this](const boost::system::error_code& ec) {
|
||||||
if (ec) return;
|
if (ec) return;
|
||||||
update_world(timer, ioc);
|
update_world();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ServerBox> generateServerBoxes(int count) {
|
std::vector<ServerBox> Server::generateServerBoxes(int count) {
|
||||||
std::vector<ServerBox> boxes;
|
std::vector<ServerBox> boxes;
|
||||||
std::random_device rd;
|
std::random_device rd;
|
||||||
std::mt19937 gen(rd());
|
std::mt19937 gen(rd());
|
||||||
@@ -889,34 +789,42 @@ std::vector<ServerBox> generateServerBoxes(int count) {
|
|||||||
return boxes;
|
return boxes;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
Server::Server(tcp::acceptor& acceptor, net::io_context& ioc)
|
||||||
try {
|
: acceptor_(acceptor)
|
||||||
|
, ioc_(ioc)
|
||||||
|
, timer(ioc_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::init()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(g_boxes_mutex);
|
std::lock_guard<std::mutex> lock(g_boxes_mutex);
|
||||||
g_serverBoxes = generateServerBoxes(50);
|
g_serverBoxes = generateServerBoxes(50);
|
||||||
//g_serverBoxes = generateServerBoxes(1);
|
|
||||||
std::cout << "Generated " << g_serverBoxes.size() << " boxes on server\n";
|
std::cout << "Generated " << g_serverBoxes.size() << " boxes on server\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Server::accept()
|
||||||
|
{
|
||||||
|
acceptor_.async_accept([&](beast::error_code ec, tcp::socket socket) {
|
||||||
|
if (!ec) {
|
||||||
|
std::make_shared<Session>(*this, std::move(socket), next_id++)->run();
|
||||||
|
}
|
||||||
|
accept();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
try {
|
||||||
net::io_context ioc;
|
net::io_context ioc;
|
||||||
tcp::acceptor acceptor{ ioc, {tcp::v4(), 8010} };
|
tcp::acceptor acceptor{ ioc, {tcp::v4(), 8010} };
|
||||||
|
|
||||||
int next_id = 1000;
|
Server server(acceptor, ioc);
|
||||||
|
|
||||||
|
server.accept();
|
||||||
|
|
||||||
std::cout << "Server started on port 8010...\n";
|
std::cout << "Server started on port 8010...\n";
|
||||||
|
|
||||||
auto do_accept = [&](auto& self_fn) -> void {
|
server.update_world();
|
||||||
acceptor.async_accept([&, self_fn](beast::error_code ec, tcp::socket socket) {
|
|
||||||
if (!ec) {
|
|
||||||
std::make_shared<Session>(std::move(socket), next_id++)->run();
|
|
||||||
}
|
|
||||||
self_fn(self_fn);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
net::steady_timer timer(ioc);
|
|
||||||
update_world(timer, ioc);
|
|
||||||
|
|
||||||
do_accept(do_accept);
|
|
||||||
ioc.run();
|
ioc.run();
|
||||||
}
|
}
|
||||||
catch (std::exception const& e) {
|
catch (std::exception const& e) {
|
||||||
|
|||||||
+140
@@ -0,0 +1,140 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <boost/beast/core.hpp>
|
||||||
|
#include <boost/beast/websocket.hpp>
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include <mutex>
|
||||||
|
#include <queue>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <Eigen/Dense>
|
||||||
|
#include "../src/network/ClientState.h"
|
||||||
|
#define _USE_MATH_DEFINES
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
namespace beast = boost::beast;
|
||||||
|
namespace http = beast::http;
|
||||||
|
namespace websocket = beast::websocket;
|
||||||
|
namespace net = boost::asio;
|
||||||
|
using tcp = net::ip::tcp;
|
||||||
|
static constexpr float kWorldZOffset = 45000.0f;
|
||||||
|
static const Eigen::Vector3f kWorldOffset(0.0f, 0.0f, kWorldZOffset);
|
||||||
|
|
||||||
|
static constexpr float kShipRadius = 15.0f;
|
||||||
|
static constexpr float kSpawnShipMargin = 25.0f;
|
||||||
|
static constexpr float kSpawnBoxMargin = 15.0f;
|
||||||
|
static constexpr float kSpawnZJitter = 60.0f;
|
||||||
|
|
||||||
|
std::vector<std::string> split(const std::string& s, char delimiter);
|
||||||
|
|
||||||
|
struct DeathInfo {
|
||||||
|
int targetId = -1;
|
||||||
|
uint64_t serverTime = 0;
|
||||||
|
Eigen::Vector3f position = Eigen::Vector3f::Zero();
|
||||||
|
int killerId = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct ServerBox {
|
||||||
|
Eigen::Vector3f position;
|
||||||
|
Eigen::Matrix3f rotation;
|
||||||
|
float collisionRadius = 2.0f;
|
||||||
|
bool destroyed = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Projectile {
|
||||||
|
int shooterId = -1;
|
||||||
|
uint64_t spawnMs = 0;
|
||||||
|
Eigen::Vector3f pos;
|
||||||
|
Eigen::Vector3f vel;
|
||||||
|
float lifeMs = PROJECTILE_LIFE;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BoxDestroyedInfo {
|
||||||
|
int boxIndex = -1;
|
||||||
|
uint64_t serverTime = 0;
|
||||||
|
Eigen::Vector3f position = Eigen::Vector3f::Zero();
|
||||||
|
int destroyedBy = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Server;
|
||||||
|
|
||||||
|
class Session : public std::enable_shared_from_this<Session> {
|
||||||
|
Server& server_;
|
||||||
|
websocket::stream<beast::tcp_stream> ws_;
|
||||||
|
beast::flat_buffer buffer_;
|
||||||
|
int id_;
|
||||||
|
bool is_writing_ = false;
|
||||||
|
std::queue<std::shared_ptr<std::string>> writeQueue_;
|
||||||
|
std::mutex writeMutex_;
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
ClientStateInterval timedClientStates;
|
||||||
|
bool joined_ = false;
|
||||||
|
std::chrono::system_clock::time_point lastReceivedTime_;
|
||||||
|
|
||||||
|
bool hasReservedSpawn_ = false;
|
||||||
|
Eigen::Vector3f reservedSpawn_ = Eigen::Vector3f(0.0f, 0.0f, kWorldZOffset);
|
||||||
|
|
||||||
|
std::string nickname = "Player";
|
||||||
|
int shipType = 0;
|
||||||
|
|
||||||
|
Session(Server& server, tcp::socket&& socket, int id);
|
||||||
|
int get_id() const;
|
||||||
|
bool hasSpawnReserved() const;
|
||||||
|
const Eigen::Vector3f& reservedSpawn() const;
|
||||||
|
bool fetchStateAtTime(std::chrono::system_clock::time_point targetTime, ClientState& outState) const;
|
||||||
|
void send_message(const std::string& msg);
|
||||||
|
void run();
|
||||||
|
bool IsMessageValid(const std::string& fullMessage);
|
||||||
|
bool is_timed_out(std::chrono::system_clock::time_point now) const;
|
||||||
|
void force_disconnect();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void sendBoxesToClient();
|
||||||
|
public:
|
||||||
|
void init();
|
||||||
|
ClientState get_latest_state(std::chrono::system_clock::time_point now);
|
||||||
|
void doWrite();
|
||||||
|
private:
|
||||||
|
|
||||||
|
void do_read();
|
||||||
|
void process_message(const std::string& msg);
|
||||||
|
};
|
||||||
|
|
||||||
|
class Server
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
tcp::acceptor& acceptor_;
|
||||||
|
net::io_context& ioc_;
|
||||||
|
net::steady_timer timer;
|
||||||
|
std::vector<BoxDestroyedInfo> g_boxDestructions;
|
||||||
|
std::mutex g_boxDestructions_mutex;
|
||||||
|
|
||||||
|
std::vector<ServerBox> g_serverBoxes;
|
||||||
|
std::mutex g_boxes_mutex;
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<Session>> g_sessions;
|
||||||
|
std::mutex g_sessions_mutex;
|
||||||
|
|
||||||
|
std::vector<Projectile> g_projectiles;
|
||||||
|
std::mutex g_projectiles_mutex;
|
||||||
|
|
||||||
|
std::unordered_set<int> g_dead_players;
|
||||||
|
std::mutex g_dead_mutex;
|
||||||
|
|
||||||
|
int next_id = 1000;
|
||||||
|
|
||||||
|
std::vector<ServerBox> generateServerBoxes(int count);
|
||||||
|
public:
|
||||||
|
Server(tcp::acceptor& acceptor, net::io_context& ioc);
|
||||||
|
|
||||||
|
void broadcastToAll(const std::string& message);
|
||||||
|
void broadcastToAllExceptId(const std::string& message, int id);
|
||||||
|
void createProjectile(int id, Eigen::Vector3f pos, Eigen::Quaternionf dir, float velocity);
|
||||||
|
void update_world();
|
||||||
|
Eigen::Vector3f PickSafeSpawnPos(int forPlayerId);
|
||||||
|
void init();
|
||||||
|
void accept();
|
||||||
|
};
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
#include "render/Renderer.h"
|
#include "render/Renderer.h"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
|
|
||||||
#include "AudioPlayerAsync.h"
|
#include "AudioPlayerAsync.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "BoneAnimatedModel.h"
|
#include "BoneAnimatedModel.h"
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "render/Renderer.h"
|
#include "render/Renderer.h"
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
#include "Environment.h"
|
#include "Environment.h"
|
||||||
|
|
||||||
#include "utils/Utils.h"
|
#include "utils/Utils.h"
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -150,6 +150,18 @@ namespace ZL
|
|||||||
Environment::shipState.nickname = nickname;
|
Environment::shipState.nickname = nickname;
|
||||||
Environment::shipState.shipType = shipType;
|
Environment::shipState.shipType = shipType;
|
||||||
|
|
||||||
|
|
||||||
|
if (Environment::shipState.shipType == 1)
|
||||||
|
{
|
||||||
|
menuManager.uiManager.findButton("shootButton")->state = ButtonState::Disabled;
|
||||||
|
menuManager.uiManager.findButton("shootButton2")->state = ButtonState::Disabled;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
menuManager.uiManager.findButton("shootButton")->state = ButtonState::Normal;
|
||||||
|
menuManager.uiManager.findButton("shootButton2")->state = ButtonState::Normal;
|
||||||
|
}
|
||||||
|
|
||||||
auto localClient = new LocalClient;
|
auto localClient = new LocalClient;
|
||||||
ClientState st = Environment::shipState;
|
ClientState st = Environment::shipState;
|
||||||
st.id = localClient->GetClientId();
|
st.id = localClient->GetClientId();
|
||||||
@@ -230,6 +242,10 @@ namespace ZL
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Game::drawScene() {
|
void Game::drawScene() {
|
||||||
|
//For low quality:
|
||||||
|
glViewport(0, 0, Environment::width, Environment::height);
|
||||||
|
//For high quality:
|
||||||
|
//glViewport(0, 0, Environment::projectionWidth, Environment::projectionHeight);
|
||||||
if (!loadingCompleted) {
|
if (!loadingCompleted) {
|
||||||
drawLoading();
|
drawLoading();
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-1
@@ -1,4 +1,4 @@
|
|||||||
#include "MenuManager.h"
|
#include "MenuManager.h"
|
||||||
|
|
||||||
|
|
||||||
namespace ZL {
|
namespace ZL {
|
||||||
@@ -103,6 +103,22 @@ namespace ZL {
|
|||||||
uiManager.setButtonCallback("shootButton2", [this](const std::string& name) {
|
uiManager.setButtonCallback("shootButton2", [this](const std::string& name) {
|
||||||
onFirePressed();
|
onFirePressed();
|
||||||
});
|
});
|
||||||
|
uiManager.setButtonCallback("plusButton", [this](const std::string& name) {
|
||||||
|
int newVel = Environment::shipState.selectedVelocity+1;
|
||||||
|
if (newVel > 4)
|
||||||
|
{
|
||||||
|
newVel = 4;
|
||||||
|
}
|
||||||
|
onVelocityChanged(newVel);
|
||||||
|
});
|
||||||
|
uiManager.setButtonCallback("minusButton", [this](const std::string& name) {
|
||||||
|
int newVel = Environment::shipState.selectedVelocity-1;
|
||||||
|
if (newVel < 0)
|
||||||
|
{
|
||||||
|
newVel = 0;
|
||||||
|
}
|
||||||
|
onVelocityChanged(newVel);
|
||||||
|
});
|
||||||
uiManager.setSliderCallback("velocitySlider", [this](const std::string& name, float value) {
|
uiManager.setSliderCallback("velocitySlider", [this](const std::string& name, float value) {
|
||||||
|
|
||||||
int newVel = roundf(value * 10);
|
int newVel = roundf(value * 10);
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "render/Renderer.h"
|
#include "render/Renderer.h"
|
||||||
#include "Environment.h"
|
#include "Environment.h"
|
||||||
#include "render/TextureManager.h"
|
#include "render/TextureManager.h"
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
#include "Projectile.h"
|
#include "Projectile.h"
|
||||||
|
|
||||||
namespace ZL {
|
namespace ZL {
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "render/Renderer.h"
|
#include "render/Renderer.h"
|
||||||
#include "render/TextureManager.h"
|
#include "render/TextureManager.h"
|
||||||
|
|||||||
+83
-11
@@ -1,4 +1,4 @@
|
|||||||
#include "Space.h"
|
#include "Space.h"
|
||||||
#include "AnimatedModel.h"
|
#include "AnimatedModel.h"
|
||||||
#include "BoneAnimatedModel.h"
|
#include "BoneAnimatedModel.h"
|
||||||
#include "planet/PlanetData.h"
|
#include "planet/PlanetData.h"
|
||||||
@@ -282,6 +282,20 @@ namespace ZL
|
|||||||
|
|
||||||
menuManager.onVelocityChanged = [this](float newVelocity) {
|
menuManager.onVelocityChanged = [this](float newVelocity) {
|
||||||
newShipVelocity = newVelocity;
|
newShipVelocity = newVelocity;
|
||||||
|
if (Environment::shipState.shipType == 0)
|
||||||
|
{
|
||||||
|
if (newVelocity > 2)
|
||||||
|
{
|
||||||
|
this->menuManager.uiManager.findButton("shootButton")->state = ButtonState::Disabled;
|
||||||
|
this->menuManager.uiManager.findButton("shootButton2")->state = ButtonState::Disabled;
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->menuManager.uiManager.findButton("shootButton")->state = ButtonState::Normal;
|
||||||
|
this->menuManager.uiManager.findButton("shootButton2")->state = ButtonState::Normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
menuManager.onFirePressed = [this]() {
|
menuManager.onFirePressed = [this]() {
|
||||||
@@ -289,10 +303,13 @@ namespace ZL
|
|||||||
};
|
};
|
||||||
|
|
||||||
bool cfgLoaded = sparkEmitter.loadFromJsonFile("resources/config/spark_config.json", renderer, CONST_ZIP_FILE);
|
bool cfgLoaded = sparkEmitter.loadFromJsonFile("resources/config/spark_config.json", renderer, CONST_ZIP_FILE);
|
||||||
|
bool cfgLoaded2 = sparkEmitterCargo.loadFromJsonFile("resources/config/spark_config_cargo.json", renderer, CONST_ZIP_FILE);
|
||||||
bool projCfgLoaded = projectileEmitter.loadFromJsonFile("resources/config/spark_projectile_config.json", renderer, CONST_ZIP_FILE);
|
bool projCfgLoaded = projectileEmitter.loadFromJsonFile("resources/config/spark_projectile_config.json", renderer, CONST_ZIP_FILE);
|
||||||
bool explosionCfgLoaded = explosionEmitter.loadFromJsonFile("resources/config/explosion_config.json", renderer, CONST_ZIP_FILE);
|
bool explosionCfgLoaded = explosionEmitter.loadFromJsonFile("resources/config/explosion_config.json", renderer, CONST_ZIP_FILE);
|
||||||
explosionEmitter.setEmissionPoints(std::vector<Vector3f>());
|
explosionEmitter.setEmissionPoints(std::vector<Vector3f>());
|
||||||
projectileEmitter.setEmissionPoints(std::vector<Vector3f>());
|
//projectileEmitter.setEmissionPoints({ Vector3f{0,0,45000} });
|
||||||
|
//projectileEmitter.setUseWorldSpace(true);
|
||||||
|
|
||||||
|
|
||||||
cubemapTexture = std::make_shared<Texture>(
|
cubemapTexture = std::make_shared<Texture>(
|
||||||
std::array<TextureDataStruct, 6>{
|
std::array<TextureDataStruct, 6>{
|
||||||
@@ -315,7 +332,9 @@ namespace ZL
|
|||||||
spaceshipBase = LoadFromTextFile02("resources/spaceshipnew001.txt", CONST_ZIP_FILE);
|
spaceshipBase = LoadFromTextFile02("resources/spaceshipnew001.txt", CONST_ZIP_FILE);
|
||||||
spaceshipBase.RotateByMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(M_PI, Eigen::Vector3f::UnitY())).toRotationMatrix());// QuatFromRotateAroundY(M_PI / 2.0).toRotationMatrix());
|
spaceshipBase.RotateByMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(M_PI, Eigen::Vector3f::UnitY())).toRotationMatrix());// QuatFromRotateAroundY(M_PI / 2.0).toRotationMatrix());
|
||||||
|
|
||||||
|
|
||||||
spaceshipBase.Move(Vector3f{ 1.2, 0, -5 });
|
spaceshipBase.Move(Vector3f{ 1.2, 0, -5 });
|
||||||
|
spaceshipBase.Scale(0.4f);
|
||||||
|
|
||||||
spaceship.AssignFrom(spaceshipBase);
|
spaceship.AssignFrom(spaceshipBase);
|
||||||
spaceship.RefreshVBO();
|
spaceship.RefreshVBO();
|
||||||
@@ -501,12 +520,18 @@ namespace ZL
|
|||||||
renderer.EnableVertexAttribArray(vTexCoordName);
|
renderer.EnableVertexAttribArray(vTexCoordName);
|
||||||
glEnable(GL_BLEND);
|
glEnable(GL_BLEND);
|
||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
||||||
|
renderer.PushMatrix();
|
||||||
|
renderer.LoadIdentity();
|
||||||
|
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
|
||||||
|
renderer.RotateMatrix(Environment::inverseShipMatrix);
|
||||||
|
renderer.TranslateMatrix(-Environment::shipState.position);
|
||||||
for (const auto& p : projectiles) {
|
for (const auto& p : projectiles) {
|
||||||
if (p && p->isActive()) {
|
if (p && p->isActive()) {
|
||||||
p->draw(renderer);
|
//p->draw(renderer);
|
||||||
p->projectileEmitter.draw(renderer, Environment::zoom, Environment::width, Environment::height);
|
p->projectileEmitter.draw(renderer, Environment::zoom, Environment::width, Environment::height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
renderer.PopMatrix();
|
||||||
glDisable(GL_BLEND);
|
glDisable(GL_BLEND);
|
||||||
|
|
||||||
renderer.DisableVertexAttribArray(vPositionName);
|
renderer.DisableVertexAttribArray(vPositionName);
|
||||||
@@ -519,12 +544,17 @@ namespace ZL
|
|||||||
renderer.PushMatrix();
|
renderer.PushMatrix();
|
||||||
renderer.TranslateMatrix({ 0, 0, 16 });
|
renderer.TranslateMatrix({ 0, 0, 16 });
|
||||||
renderer.TranslateMatrix({ 0, -6.f, 0 });
|
renderer.TranslateMatrix({ 0, -6.f, 0 });
|
||||||
sparkEmitter.draw(renderer, Environment::zoom, Environment::width, Environment::height);
|
if (Environment::shipState.shipType == 1) {
|
||||||
|
sparkEmitterCargo.draw(renderer, Environment::zoom, Environment::width, Environment::height, false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sparkEmitter.draw(renderer, Environment::zoom, Environment::width, Environment::height, false);
|
||||||
|
}
|
||||||
renderer.PopMatrix();
|
renderer.PopMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showExplosion) {
|
if (showExplosion) {
|
||||||
explosionEmitter.draw(renderer, Environment::zoom, Environment::width, Environment::height);
|
explosionEmitter.draw(renderer, Environment::zoom, Environment::width, Environment::height, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
//glBindTexture(GL_TEXTURE_2D, basePlatformTexture->getTexID());
|
//glBindTexture(GL_TEXTURE_2D, basePlatformTexture->getTexID());
|
||||||
@@ -1450,7 +1480,17 @@ namespace ZL
|
|||||||
|
|
||||||
auto now_ms = newTickCount;
|
auto now_ms = newTickCount;
|
||||||
|
|
||||||
sparkEmitter.update(static_cast<float>(delta));
|
SparkEmitter* sparkEmitterPtr;
|
||||||
|
|
||||||
|
if (Environment::shipState.shipType == 1) {
|
||||||
|
sparkEmitterPtr = &sparkEmitterCargo;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sparkEmitterPtr = &sparkEmitter;
|
||||||
|
|
||||||
|
}
|
||||||
|
sparkEmitterPtr->update(static_cast<float>(delta));
|
||||||
planetObject.update(static_cast<float>(delta));
|
planetObject.update(static_cast<float>(delta));
|
||||||
|
|
||||||
if (firePressed)
|
if (firePressed)
|
||||||
@@ -1594,9 +1634,9 @@ namespace ZL
|
|||||||
for (const auto& p : projectiles) {
|
for (const auto& p : projectiles) {
|
||||||
if (p && p->isActive()) {
|
if (p && p->isActive()) {
|
||||||
Vector3f worldPos = p->getPosition();
|
Vector3f worldPos = p->getPosition();
|
||||||
Vector3f rel = worldPos - Environment::shipState.position;
|
//Vector3f rel = worldPos - Environment::shipState.position;
|
||||||
Vector3f camPos = Environment::inverseShipMatrix * rel;
|
//Vector3f camPos = Environment::inverseShipMatrix * rel;
|
||||||
p->projectileEmitter.setEmissionPoints({ camPos });
|
p->projectileEmitter.setEmissionPoints({ worldPos });
|
||||||
p->projectileEmitter.emit();
|
p->projectileEmitter.emit();
|
||||||
p->projectileEmitter.update(static_cast<float>(delta));
|
p->projectileEmitter.update(static_cast<float>(delta));
|
||||||
}
|
}
|
||||||
@@ -1610,16 +1650,25 @@ namespace ZL
|
|||||||
projectileEmitter.setEmissionPoints(std::vector<Vector3f>());
|
projectileEmitter.setEmissionPoints(std::vector<Vector3f>());
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
|
if (Environment::shipState.velocity > 0.1f) {
|
||||||
|
|
||||||
|
sparkEmitterPtr->setIsActive(true);
|
||||||
|
|
||||||
std::vector<Vector3f> shipCameraPoints;
|
std::vector<Vector3f> shipCameraPoints;
|
||||||
for (const auto& lp : shipLocalEmissionPoints) {
|
for (const auto& lp : shipLocalEmissionPoints) {
|
||||||
Vector3f adjusted = lp + Vector3f{ 0.0f, -Environment::zoom * 0.03f, 0.0f };
|
Vector3f adjusted = lp + Vector3f{ 0.0f, -Environment::zoom * 0.03f, 0.0f };
|
||||||
shipCameraPoints.push_back(adjusted);
|
shipCameraPoints.push_back(adjusted);
|
||||||
}
|
}
|
||||||
if (!shipCameraPoints.empty()) {
|
if (!shipCameraPoints.empty()) {
|
||||||
sparkEmitter.setEmissionPoints(shipCameraPoints);
|
sparkEmitterPtr->setEmissionPoints(shipCameraPoints);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sparkEmitterPtr->setIsActive(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
sparkEmitter.update(static_cast<float>(delta));
|
sparkEmitterPtr->update(static_cast<float>(delta));
|
||||||
//projectileEmitter.update(static_cast<float>(delta));
|
//projectileEmitter.update(static_cast<float>(delta));
|
||||||
|
|
||||||
explosionEmitter.update(static_cast<float>(delta));
|
explosionEmitter.update(static_cast<float>(delta));
|
||||||
@@ -1773,6 +1822,18 @@ namespace ZL
|
|||||||
|
|
||||||
void Space::update() {
|
void Space::update() {
|
||||||
if (networkClient) {
|
if (networkClient) {
|
||||||
|
if (networkClient->IsConnected()) {
|
||||||
|
wasConnectedToServer = true;
|
||||||
|
}
|
||||||
|
else if (wasConnectedToServer && shipAlive && !gameOver) {
|
||||||
|
wasConnectedToServer = false;
|
||||||
|
shipAlive = false;
|
||||||
|
gameOver = true;
|
||||||
|
Environment::shipState.velocity = 0.0f;
|
||||||
|
std::cout << "Client: Lost connection to server\n";
|
||||||
|
menuManager.showGameOver(this->playerScore);
|
||||||
|
}
|
||||||
|
|
||||||
auto pending = networkClient->getPendingProjectiles();
|
auto pending = networkClient->getPendingProjectiles();
|
||||||
if (!pending.empty()) {
|
if (!pending.empty()) {
|
||||||
const float projectileSpeed = PROJECTILE_VELOCITY;
|
const float projectileSpeed = PROJECTILE_VELOCITY;
|
||||||
@@ -1862,6 +1923,17 @@ namespace ZL
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto disconnects = networkClient->getPendingDisconnects();
|
||||||
|
for (int pid : disconnects) {
|
||||||
|
remotePlayerStates.erase(pid);
|
||||||
|
deadRemotePlayers.erase(pid);
|
||||||
|
if (trackedTargetId == pid) {
|
||||||
|
trackedTargetId = -1;
|
||||||
|
targetAcquireAnim = 0.f;
|
||||||
|
}
|
||||||
|
std::cout << "Client: Remote player " << pid << " left the game, removed from scene\n";
|
||||||
|
}
|
||||||
|
|
||||||
auto boxDestructions = networkClient->getPendingBoxDestructions();
|
auto boxDestructions = networkClient->getPendingBoxDestructions();
|
||||||
if (!boxDestructions.empty()) {
|
if (!boxDestructions.empty()) {
|
||||||
std::cout << "Game: Received " << boxDestructions.size() << " box destruction events" << std::endl;
|
std::cout << "Game: Received " << boxDestructions.size() << " box destruction events" << std::endl;
|
||||||
|
|||||||
+3
-1
@@ -1,4 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "render/Renderer.h"
|
#include "render/Renderer.h"
|
||||||
#include "Environment.h"
|
#include "Environment.h"
|
||||||
@@ -88,6 +88,7 @@ namespace ZL {
|
|||||||
VertexDataStruct boxBase;
|
VertexDataStruct boxBase;
|
||||||
|
|
||||||
SparkEmitter sparkEmitter;
|
SparkEmitter sparkEmitter;
|
||||||
|
SparkEmitter sparkEmitterCargo;
|
||||||
SparkEmitter projectileEmitter;
|
SparkEmitter projectileEmitter;
|
||||||
SparkEmitter explosionEmitter;
|
SparkEmitter explosionEmitter;
|
||||||
PlanetObject planetObject;
|
PlanetObject planetObject;
|
||||||
@@ -123,6 +124,7 @@ namespace ZL {
|
|||||||
|
|
||||||
std::unordered_set<int> deadRemotePlayers;
|
std::unordered_set<int> deadRemotePlayers;
|
||||||
int playerScore = 0;
|
int playerScore = 0;
|
||||||
|
bool wasConnectedToServer = false;
|
||||||
|
|
||||||
static constexpr float TARGET_MAX_DIST = 50000.0f;
|
static constexpr float TARGET_MAX_DIST = 50000.0f;
|
||||||
static constexpr float TARGET_MAX_DIST_SQ = TARGET_MAX_DIST * TARGET_MAX_DIST;
|
static constexpr float TARGET_MAX_DIST_SQ = TARGET_MAX_DIST * TARGET_MAX_DIST;
|
||||||
|
|||||||
+34
-4
@@ -74,7 +74,7 @@ namespace ZL {
|
|||||||
texture = tex;
|
texture = tex;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SparkEmitter::prepareDrawData() {
|
void SparkEmitter::prepareDrawData(bool withRotation) {
|
||||||
if (!drawDataDirty) return;
|
if (!drawDataDirty) return;
|
||||||
|
|
||||||
drawPositions.clear();
|
drawPositions.clear();
|
||||||
@@ -91,7 +91,7 @@ namespace ZL {
|
|||||||
for (const auto& particle : particles) {
|
for (const auto& particle : particles) {
|
||||||
if (particle.active) {
|
if (particle.active) {
|
||||||
Vector3f posCam;
|
Vector3f posCam;
|
||||||
if (useWorldSpace) {
|
if (withRotation) {
|
||||||
Vector3f rel = particle.position - Environment::shipState.position;
|
Vector3f rel = particle.position - Environment::shipState.position;
|
||||||
posCam = Environment::inverseShipMatrix * rel;
|
posCam = Environment::inverseShipMatrix * rel;
|
||||||
}
|
}
|
||||||
@@ -120,6 +120,29 @@ namespace ZL {
|
|||||||
|
|
||||||
float size = particleSize * particle.scale;
|
float size = particleSize * particle.scale;
|
||||||
|
|
||||||
|
if (withRotation)
|
||||||
|
{
|
||||||
|
|
||||||
|
drawPositions.push_back(Environment::shipState.rotation * Vector3f{ -size, -size, 0 } + posCam);
|
||||||
|
drawTexCoords.push_back({ 0.0f, 0.0f });
|
||||||
|
|
||||||
|
drawPositions.push_back(Environment::shipState.rotation * Vector3f{ -size, size,0 } + posCam);
|
||||||
|
drawTexCoords.push_back({ 0.0f, 1.0f });
|
||||||
|
|
||||||
|
drawPositions.push_back(Environment::shipState.rotation * Vector3f{ size,size, 0 } + posCam);
|
||||||
|
drawTexCoords.push_back({ 1.0f, 1.0f });
|
||||||
|
|
||||||
|
drawPositions.push_back(Environment::shipState.rotation * Vector3f{ -size, -size, 0 } + posCam);
|
||||||
|
drawTexCoords.push_back({ 0.0f, 0.0f });
|
||||||
|
|
||||||
|
drawPositions.push_back(Environment::shipState.rotation * Vector3f{ size, size,0 } + posCam);
|
||||||
|
drawTexCoords.push_back({ 1.0f, 1.0f });
|
||||||
|
|
||||||
|
drawPositions.push_back(Environment::shipState.rotation * Vector3f{ size, -size, 0 } + posCam);
|
||||||
|
drawTexCoords.push_back({ 1.0f, 0.0f });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
drawPositions.push_back({ posCam(0) - size, posCam(1) - size, posCam(2) });
|
drawPositions.push_back({ posCam(0) - size, posCam(1) - size, posCam(2) });
|
||||||
drawTexCoords.push_back({ 0.0f, 0.0f });
|
drawTexCoords.push_back({ 0.0f, 0.0f });
|
||||||
|
|
||||||
@@ -137,12 +160,19 @@ namespace ZL {
|
|||||||
|
|
||||||
drawPositions.push_back({ posCam(0) + size, posCam(1) - size, posCam(2) });
|
drawPositions.push_back({ posCam(0) + size, posCam(1) - size, posCam(2) });
|
||||||
drawTexCoords.push_back({ 1.0f, 0.0f });
|
drawTexCoords.push_back({ 1.0f, 0.0f });
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
drawDataDirty = false;
|
drawDataDirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SparkEmitter::draw(Renderer& renderer, float zoom, int screenWidth, int screenHeight) {
|
void SparkEmitter::draw(Renderer& renderer, float zoom, int screenWidth, int screenHeight)
|
||||||
|
{
|
||||||
|
draw(renderer, zoom, screenWidth, screenHeight, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SparkEmitter::draw(Renderer& renderer, float zoom, int screenWidth, int screenHeight, bool withRotation) {
|
||||||
if (!configured) {
|
if (!configured) {
|
||||||
throw std::runtime_error("Failed to load spark emitter config file 1!");
|
throw std::runtime_error("Failed to load spark emitter config file 1!");
|
||||||
}
|
}
|
||||||
@@ -155,7 +185,7 @@ namespace ZL {
|
|||||||
throw std::runtime_error("Failed to load spark emitter config file 2!");
|
throw std::runtime_error("Failed to load spark emitter config file 2!");
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareDrawData();
|
prepareDrawData(withRotation);
|
||||||
|
|
||||||
if (drawPositions.empty()) {
|
if (drawPositions.empty()) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
+7
-1
@@ -50,7 +50,7 @@ namespace ZL {
|
|||||||
std::string shaderProgramName;
|
std::string shaderProgramName;
|
||||||
|
|
||||||
bool configured;
|
bool configured;
|
||||||
void prepareDrawData();
|
void prepareDrawData(bool withRotation);
|
||||||
bool useWorldSpace;
|
bool useWorldSpace;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -76,11 +76,17 @@ namespace ZL {
|
|||||||
void emit();
|
void emit();
|
||||||
|
|
||||||
void draw(Renderer& renderer, float zoom, int screenWidth, int screenHeight);
|
void draw(Renderer& renderer, float zoom, int screenWidth, int screenHeight);
|
||||||
|
void draw(Renderer& renderer, float zoom, int screenWidth, int screenHeight, bool withRotation);
|
||||||
|
|
||||||
const std::vector<SparkParticle>& getParticles() const;
|
const std::vector<SparkParticle>& getParticles() const;
|
||||||
size_t getActiveParticleCount() const;
|
size_t getActiveParticleCount() const;
|
||||||
std::shared_ptr<Texture> getTexture() const { return texture; }
|
std::shared_ptr<Texture> getTexture() const { return texture; }
|
||||||
|
|
||||||
|
void setIsActive(bool newActive)
|
||||||
|
{
|
||||||
|
isActive = newActive;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void initParticle(SparkParticle& particle, int emitterIndex);
|
void initParticle(SparkParticle& particle, int emitterIndex);
|
||||||
Vector3f getRandomVelocity(int emitterIndex);
|
Vector3f getRandomVelocity(int emitterIndex);
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
#include "TextModel.h"
|
#include "TextModel.h"
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "render/Renderer.h"
|
#include "render/Renderer.h"
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|||||||
+14
-3
@@ -1,4 +1,4 @@
|
|||||||
#include "UiManager.h"
|
#include "UiManager.h"
|
||||||
#include "utils/Utils.h"
|
#include "utils/Utils.h"
|
||||||
#include "render/TextRenderer.h"
|
#include "render/TextRenderer.h"
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
@@ -57,6 +57,7 @@ namespace ZL {
|
|||||||
case ButtonState::Normal: tex = &texNormal; break;
|
case ButtonState::Normal: tex = &texNormal; break;
|
||||||
case ButtonState::Hover: tex = &texHover; break;
|
case ButtonState::Hover: tex = &texHover; break;
|
||||||
case ButtonState::Pressed: tex = &texPressed; break;
|
case ButtonState::Pressed: tex = &texPressed; break;
|
||||||
|
case ButtonState::Disabled: tex = &texDisabled; break;
|
||||||
}
|
}
|
||||||
if (!(*tex)) return;
|
if (!(*tex)) return;
|
||||||
|
|
||||||
@@ -290,6 +291,9 @@ namespace ZL {
|
|||||||
btn->texNormal = loadTex("normal");
|
btn->texNormal = loadTex("normal");
|
||||||
btn->texHover = loadTex("hover");
|
btn->texHover = loadTex("hover");
|
||||||
btn->texPressed = loadTex("pressed");
|
btn->texPressed = loadTex("pressed");
|
||||||
|
btn->texDisabled = loadTex("disabled");
|
||||||
|
|
||||||
|
btn->border = j.value("border", 0.0f);
|
||||||
|
|
||||||
node->button = btn;
|
node->button = btn;
|
||||||
}
|
}
|
||||||
@@ -1049,13 +1053,16 @@ namespace ZL {
|
|||||||
|
|
||||||
void UiManager::onMouseMove(int x, int y) {
|
void UiManager::onMouseMove(int x, int y) {
|
||||||
for (auto& b : buttons) {
|
for (auto& b : buttons) {
|
||||||
if (b->rect.contains((float)x, (float)y)) {
|
if (b->state != ButtonState::Disabled)
|
||||||
|
{
|
||||||
|
if (b->rect.containsConsideringBorder((float)x, (float)y, b->border)) {
|
||||||
if (b->state != ButtonState::Pressed) b->state = ButtonState::Hover;
|
if (b->state != ButtonState::Pressed) b->state = ButtonState::Hover;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (b->state != ButtonState::Pressed) b->state = ButtonState::Normal;
|
if (b->state != ButtonState::Pressed) b->state = ButtonState::Normal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (pressedSlider) {
|
if (pressedSlider) {
|
||||||
auto s = pressedSlider;
|
auto s = pressedSlider;
|
||||||
@@ -1074,13 +1081,17 @@ namespace ZL {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void UiManager::onMouseDown(int x, int y) {
|
void UiManager::onMouseDown(int x, int y) {
|
||||||
for (auto& b : buttons) {
|
for (auto& b : buttons) {
|
||||||
if (b->rect.contains((float)x, (float)y)) {
|
if (b->state != ButtonState::Disabled)
|
||||||
|
{
|
||||||
|
if (b->rect.containsConsideringBorder((float)x, (float)y, b->border)) {
|
||||||
b->state = ButtonState::Pressed;
|
b->state = ButtonState::Pressed;
|
||||||
pressedButton = b;
|
pressedButton = b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (auto& s : sliders) {
|
for (auto& s : sliders) {
|
||||||
if (s->rect.contains((float)x, (float)y)) {
|
if (s->rect.contains((float)x, (float)y)) {
|
||||||
|
|||||||
+9
-2
@@ -1,4 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "render/Renderer.h"
|
#include "render/Renderer.h"
|
||||||
#include "render/TextureManager.h"
|
#include "render/TextureManager.h"
|
||||||
@@ -23,12 +23,17 @@ namespace ZL {
|
|||||||
bool contains(float px, float py) const {
|
bool contains(float px, float py) const {
|
||||||
return px >= x && px <= x + w && py >= y && py <= y + h;
|
return px >= x && px <= x + w && py >= y && py <= y + h;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool containsConsideringBorder(float px, float py, float border) const {
|
||||||
|
return px >= x+border && px <= x + w-border && py >= y+border && py <= y + h-border;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class ButtonState {
|
enum class ButtonState {
|
||||||
Normal,
|
Normal,
|
||||||
Hover,
|
Hover,
|
||||||
Pressed
|
Pressed,
|
||||||
|
Disabled
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class LayoutType {
|
enum class LayoutType {
|
||||||
@@ -76,9 +81,11 @@ namespace ZL {
|
|||||||
struct UiButton {
|
struct UiButton {
|
||||||
std::string name;
|
std::string name;
|
||||||
UiRect rect;
|
UiRect rect;
|
||||||
|
float border = 0;
|
||||||
std::shared_ptr<Texture> texNormal;
|
std::shared_ptr<Texture> texNormal;
|
||||||
std::shared_ptr<Texture> texHover;
|
std::shared_ptr<Texture> texHover;
|
||||||
std::shared_ptr<Texture> texPressed;
|
std::shared_ptr<Texture> texPressed;
|
||||||
|
std::shared_ptr<Texture> texDisabled;
|
||||||
ButtonState state = ButtonState::Normal;
|
ButtonState state = ButtonState::Normal;
|
||||||
|
|
||||||
VertexRenderStruct mesh;
|
VertexRenderStruct mesh;
|
||||||
|
|||||||
+7
-7
@@ -1,4 +1,4 @@
|
|||||||
#include "Game.h"
|
#include "Game.h"
|
||||||
#include "Environment.h"
|
#include "Environment.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
@@ -26,10 +26,6 @@ ZL::Game game;
|
|||||||
|
|
||||||
#ifdef EMSCRIPTEN
|
#ifdef EMSCRIPTEN
|
||||||
void MainLoop() {
|
void MainLoop() {
|
||||||
// SDL_GL_MakeCurrent тут не нужен каждый раз
|
|
||||||
/*glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
||||||
SDL_GL_SwapWindow(ZL::Environment::window);*/
|
|
||||||
g_game->update();
|
g_game->update();
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
@@ -55,8 +51,8 @@ EM_BOOL onWebGLContextRestored(int /*eventType*/, const void* /*reserved*/, void
|
|||||||
|
|
||||||
static void applyResize(int logicalW, int logicalH) {
|
static void applyResize(int logicalW, int logicalH) {
|
||||||
// Получаем коэффициент плотности пикселей (например, 2.625 на Pixel или 3.0 на Samsung)
|
// Получаем коэффициент плотности пикселей (например, 2.625 на Pixel или 3.0 на Samsung)
|
||||||
//double dpr = emscripten_get_device_pixel_ratio();
|
double dpr = emscripten_get_device_pixel_ratio();
|
||||||
double dpr = 1; // low quality
|
//double dpr = 1; // low quality
|
||||||
|
|
||||||
// Вычисляем реальные физические пиксели
|
// Вычисляем реальные физические пиксели
|
||||||
int physicalW = static_cast<int>(logicalW * dpr);
|
int physicalW = static_cast<int>(logicalW * dpr);
|
||||||
@@ -83,6 +79,10 @@ static void applyResize(int logicalW, int logicalH) {
|
|||||||
e.window.data1 = physicalW;
|
e.window.data1 = physicalW;
|
||||||
e.window.data2 = physicalH;
|
e.window.data2 = physicalH;
|
||||||
SDL_PushEvent(&e);
|
SDL_PushEvent(&e);
|
||||||
|
|
||||||
|
std::cout << "Resized, new size: " << logicalW << "x" << logicalH
|
||||||
|
<< " (physical: " << physicalW << "x" << physicalH
|
||||||
|
<< ", DPR: " << dpr << ")" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
EM_BOOL onWindowResized(int /*eventType*/, const EmscriptenUiEvent* e, void* /*userData*/) {
|
EM_BOOL onWindowResized(int /*eventType*/, const EmscriptenUiEvent* e, void* /*userData*/) {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ constexpr auto NET_SECRET = "880b3713b9ff3e7a94b2712d54679e1f";
|
|||||||
#define ENABLE_NETWORK_CHECKSUM
|
#define ENABLE_NETWORK_CHECKSUM
|
||||||
|
|
||||||
constexpr float ANGULAR_ACCEL = 0.005f * 1000.0f;
|
constexpr float ANGULAR_ACCEL = 0.005f * 1000.0f;
|
||||||
constexpr float SHIP_ACCEL = 1.0f * 1000.0f;
|
constexpr float SHIP_ACCEL = 1.0f * 200.0f;
|
||||||
constexpr float ROTATION_SENSITIVITY = 0.002f;
|
constexpr float ROTATION_SENSITIVITY = 0.002f;
|
||||||
|
|
||||||
constexpr float PLANET_RADIUS = 20000.f;
|
constexpr float PLANET_RADIUS = 20000.f;
|
||||||
@@ -26,6 +26,7 @@ constexpr float PITCH_LIMIT = static_cast<float>(M_PI) / 9.f;//18.0f;
|
|||||||
constexpr long long SERVER_DELAY = 0; //ms
|
constexpr long long SERVER_DELAY = 0; //ms
|
||||||
constexpr long long CLIENT_DELAY = 500; //ms
|
constexpr long long CLIENT_DELAY = 500; //ms
|
||||||
constexpr long long CUTOFF_TIME = 5000; //ms
|
constexpr long long CUTOFF_TIME = 5000; //ms
|
||||||
|
constexpr long long PLAYER_TIMEOUT_MS = 10000; //ms — disconnect if no UPD received
|
||||||
|
|
||||||
constexpr float PROJECTILE_VELOCITY = 600.f;
|
constexpr float PROJECTILE_VELOCITY = 600.f;
|
||||||
constexpr float PROJECTILE_LIFE = 15000.f; //ms
|
constexpr float PROJECTILE_LIFE = 15000.f; //ms
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ namespace ZL {
|
|||||||
virtual int GetClientId() const { return -1; }
|
virtual int GetClientId() const { return -1; }
|
||||||
virtual std::vector<BoxDestroyedInfo> getPendingBoxDestructions() = 0;
|
virtual std::vector<BoxDestroyedInfo> getPendingBoxDestructions() = 0;
|
||||||
virtual int64_t getTimeOffset() const { return 0; }
|
virtual int64_t getTimeOffset() const { return 0; }
|
||||||
|
virtual std::vector<int> getPendingDisconnects() { return {}; }
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -103,6 +103,19 @@ namespace ZL {
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (msg.rfind("PLAYER_LEFT:", 0) == 0) {
|
||||||
|
if (parts.size() >= 2) {
|
||||||
|
try {
|
||||||
|
int pid = std::stoi(parts[1]);
|
||||||
|
remotePlayers.erase(pid);
|
||||||
|
pendingDisconnects_.push_back(pid);
|
||||||
|
std::cout << "Client: Player " << pid << " disconnected (PLAYER_LEFT)\n";
|
||||||
|
}
|
||||||
|
catch (...) {}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (msg.rfind("RESPAWN_ACK:", 0) == 0) {
|
if (msg.rfind("RESPAWN_ACK:", 0) == 0) {
|
||||||
//auto parts = split(msg, ':');
|
//auto parts = split(msg, ':');
|
||||||
if (parts.size() >= 2) {
|
if (parts.size() >= 2) {
|
||||||
@@ -356,6 +369,12 @@ namespace ZL {
|
|||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<int> WebSocketClientBase::getPendingDisconnects() {
|
||||||
|
std::vector<int> copy;
|
||||||
|
copy.swap(pendingDisconnects_);
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<ClientState> WebSocketClientBase::getPendingSpawns() {
|
std::vector<ClientState> WebSocketClientBase::getPendingSpawns() {
|
||||||
std::vector<ClientState> copy;
|
std::vector<ClientState> copy;
|
||||||
copy.swap(pendingSpawns_);
|
copy.swap(pendingSpawns_);
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ namespace ZL {
|
|||||||
std::vector<DeathInfo> pendingDeaths_;
|
std::vector<DeathInfo> pendingDeaths_;
|
||||||
std::vector<int> pendingRespawns_;
|
std::vector<int> pendingRespawns_;
|
||||||
std::vector<BoxDestroyedInfo> pendingBoxDestructions_;
|
std::vector<BoxDestroyedInfo> pendingBoxDestructions_;
|
||||||
|
std::vector<int> pendingDisconnects_;
|
||||||
int clientId = -1;
|
int clientId = -1;
|
||||||
int64_t timeOffset = 0;
|
int64_t timeOffset = 0;
|
||||||
std::vector<ClientState> pendingSpawns_;
|
std::vector<ClientState> pendingSpawns_;
|
||||||
@@ -41,7 +42,7 @@ namespace ZL {
|
|||||||
return serverBoxes_;
|
return serverBoxes_;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<bool> getServerBoxDestroyedFlags() {
|
std::vector<bool> getServerBoxDestroyedFlags() override {
|
||||||
return serverBoxesDestroyed_;
|
return serverBoxesDestroyed_;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,6 +50,7 @@ namespace ZL {
|
|||||||
std::vector<DeathInfo> getPendingDeaths() override;
|
std::vector<DeathInfo> getPendingDeaths() override;
|
||||||
std::vector<int> getPendingRespawns() override;
|
std::vector<int> getPendingRespawns() override;
|
||||||
std::vector<BoxDestroyedInfo> getPendingBoxDestructions() override;
|
std::vector<BoxDestroyedInfo> getPendingBoxDestructions() override;
|
||||||
|
std::vector<int> getPendingDisconnects() override;
|
||||||
std::vector<ClientState> getPendingSpawns();
|
std::vector<ClientState> getPendingSpawns();
|
||||||
int getClientId() const { return clientId; }
|
int getClientId() const { return clientId; }
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "FrameBuffer.h"
|
#include "FrameBuffer.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include "Environment.h"
|
#include "Environment.h"
|
||||||
|
|
||||||
@@ -15,10 +15,10 @@ namespace ZL {
|
|||||||
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
||||||
|
|
||||||
// Настраиваем фильтрацию
|
// Настраиваем фильтрацию
|
||||||
if (useMipmaps) {
|
if (useMipmaps) {
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||||
// Сразу генерируем пустые уровни, чтобы текстура считалась "полной"
|
// Сразу генерируем пустые уровни, чтобы текстура считалась "полной"
|
||||||
glGenerateMipmap(GL_TEXTURE_2D);
|
glGenerateMipmap(GL_TEXTURE_2D);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -49,13 +49,13 @@ namespace ZL {
|
|||||||
|
|
||||||
void FrameBuffer::Bind() {
|
void FrameBuffer::Bind() {
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||||
glViewport(0, 0, width, height); // Важно: устанавливаем вьюпорт под размер текстуры
|
glViewport(0, 0, width, height); // Важно: устанавливаем вьюпорт под размер текстуры
|
||||||
}
|
}
|
||||||
|
|
||||||
void FrameBuffer::Unbind() {
|
void FrameBuffer::Unbind() {
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
// Здесь желательно возвращать вьюпорт к размерам экрана,
|
// Здесь желательно возвращать вьюпорт к размерам экрана,
|
||||||
// например, через Environment::width/height
|
// например, через Environment::width/height
|
||||||
glViewport(0, 0, Environment::width, Environment::height);
|
glViewport(0, 0, Environment::width, Environment::height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "render/OpenGlExtensions.h"
|
#include "render/OpenGlExtensions.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "OpenGlExtensions.h"
|
#include "OpenGlExtensions.h"
|
||||||
|
|
||||||
#include "utils/Utils.h"
|
#include "utils/Utils.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
#include "SDL.h"
|
#include "SDL.h"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "render/Renderer.h"
|
#include "render/Renderer.h"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
namespace ZL {
|
namespace ZL {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "render/OpenGlExtensions.h"
|
#include "render/OpenGlExtensions.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "ShaderManager.h"
|
#include "ShaderManager.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "Perlin.h"
|
#include "Perlin.h"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <random>
|
#include <random>
|
||||||
@@ -9,19 +9,19 @@ namespace ZL {
|
|||||||
PerlinNoise::PerlinNoise() {
|
PerlinNoise::PerlinNoise() {
|
||||||
p.resize(256);
|
p.resize(256);
|
||||||
std::iota(p.begin(), p.end(), 0);
|
std::iota(p.begin(), p.end(), 0);
|
||||||
// Перемешиваем для случайности (можно задать seed)
|
// Перемешиваем для случайности (можно задать seed)
|
||||||
std::default_random_engine engine(77777);
|
std::default_random_engine engine(77777);
|
||||||
std::shuffle(p.begin(), p.end(), engine);
|
std::shuffle(p.begin(), p.end(), engine);
|
||||||
p.insert(p.end(), p.begin(), p.end()); // Дублируем для переполнения
|
p.insert(p.end(), p.begin(), p.end()); // Дублируем для переполнения
|
||||||
}
|
}
|
||||||
|
|
||||||
PerlinNoise::PerlinNoise(uint64_t seed) {
|
PerlinNoise::PerlinNoise(uint64_t seed) {
|
||||||
p.resize(256);
|
p.resize(256);
|
||||||
std::iota(p.begin(), p.end(), 0);
|
std::iota(p.begin(), p.end(), 0);
|
||||||
// Перемешиваем для случайности (используем переданный seed)
|
// Перемешиваем для случайности (используем переданный seed)
|
||||||
std::default_random_engine engine(static_cast<unsigned int>(seed));
|
std::default_random_engine engine(static_cast<unsigned int>(seed));
|
||||||
std::shuffle(p.begin(), p.end(), engine);
|
std::shuffle(p.begin(), p.end(), engine);
|
||||||
p.insert(p.end(), p.begin(), p.end()); // Дублируем для переполнения
|
p.insert(p.end(), p.begin(), p.end()); // Дублируем для переполнения
|
||||||
}
|
}
|
||||||
|
|
||||||
float PerlinNoise::fade(float t) { return t * t * t * (t * (t * 6 - 15) + 10); }
|
float PerlinNoise::fade(float t) { return t * t * t * (t * (t * 6 - 15) + 10); }
|
||||||
@@ -58,13 +58,13 @@ namespace ZL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
float PerlinNoise::getSurfaceHeight(Eigen::Vector3f pos, float noiseCoeff) {
|
float PerlinNoise::getSurfaceHeight(Eigen::Vector3f pos, float noiseCoeff) {
|
||||||
// Частота шума (чем больше, тем больше "холмов")
|
// Частота шума (чем больше, тем больше "холмов")
|
||||||
float frequency = 7.0f;
|
float frequency = 7.0f;
|
||||||
|
|
||||||
// Получаем значение шума (обычно от -1 до 1)
|
// Получаем значение шума (обычно от -1 до 1)
|
||||||
float noiseValue = noise(pos(0) * frequency, pos(1) * frequency, pos(2) * frequency);
|
float noiseValue = noise(pos(0) * frequency, pos(1) * frequency, pos(2) * frequency);
|
||||||
|
|
||||||
// Масштабируем: хотим отклонение от 1.0 до 1.1 (примерно)
|
// Масштабируем: хотим отклонение от 1.0 до 1.1 (примерно)
|
||||||
float height = 1.0f + (noiseValue * noiseCoeff);
|
float height = 1.0f + (noiseValue * noiseCoeff);
|
||||||
|
|
||||||
return height;
|
return height;
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "TaskManager.h"
|
#include "TaskManager.h"
|
||||||
|
|
||||||
|
|
||||||
namespace ZL
|
namespace ZL
|
||||||
@@ -27,8 +27,8 @@ namespace ZL
|
|||||||
|
|
||||||
TaskManager::~TaskManager() {
|
TaskManager::~TaskManager() {
|
||||||
#ifndef EMSCRIPTEN
|
#ifndef EMSCRIPTEN
|
||||||
workGuard.reset(); // Разрешаем ioContext.run() завершиться, когда задач не останется
|
workGuard.reset(); // Разрешаем ioContext.run() завершиться, когда задач не останется
|
||||||
ioContext.stop(); // Опционально: немедленная остановка
|
ioContext.stop(); // Опционально: немедленная остановка
|
||||||
for (auto& t : workers) {
|
for (auto& t : workers) {
|
||||||
if (t.joinable()) t.join();
|
if (t.joinable()) t.join();
|
||||||
}
|
}
|
||||||
@@ -51,7 +51,7 @@ namespace ZL
|
|||||||
mainThreadTasks.pop();
|
mainThreadTasks.pop();
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
// Извлекаем только одну задачу, чтобы не блокировать update надолго
|
// Извлекаем только одну задачу, чтобы не блокировать update надолго
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mainThreadMutex);
|
std::lock_guard<std::mutex> lock(mainThreadMutex);
|
||||||
if (!mainThreadTasks.empty()) {
|
if (!mainThreadTasks.empty()) {
|
||||||
@@ -62,7 +62,7 @@ namespace ZL
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (task) {
|
if (task) {
|
||||||
task(); // Здесь выполняется RefreshVBO или загрузка текстуры
|
task(); // Здесь выполняется RefreshVBO или загрузка текстуры
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifndef EMSCRIPTEN
|
#ifndef EMSCRIPTEN
|
||||||
#include <boost/asio.hpp>
|
#include <boost/asio.hpp>
|
||||||
@@ -23,7 +23,7 @@ namespace ZL {
|
|||||||
public:
|
public:
|
||||||
TaskManager(size_t threadCount = 2);
|
TaskManager(size_t threadCount = 2);
|
||||||
|
|
||||||
// Метод для добавления фоновой задачи
|
// ћетод дл¤ добавлени¤ фоновой задачи
|
||||||
void EnqueueBackgroundTask(std::function<void()> task);
|
void EnqueueBackgroundTask(std::function<void()> task);
|
||||||
|
|
||||||
// Graceful shutdown
|
// Graceful shutdown
|
||||||
@@ -49,7 +49,7 @@ namespace ZL {
|
|||||||
public:
|
public:
|
||||||
void EnqueueMainThreadTask(std::function<void()> task);
|
void EnqueueMainThreadTask(std::function<void()> task);
|
||||||
|
|
||||||
// Выполнение задач по одной (или пачкой) за кадр
|
// ¬ыполнение задач по одной (или пачкой) за кадр
|
||||||
void processMainThreadTasks();
|
void processMainThreadTasks();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|||||||
Reference in new issue
Block a user