adjust label scale: constant far, smooth growth near
This commit is contained in:
commit
c8076b6018
18 files changed
+567
-25
No files matched your search
@@ -0,0 +1,128 @@
|
||||
{
|
||||
"root": {
|
||||
"type": "FrameLayout",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"children": [
|
||||
{
|
||||
"type": "LinearLayout",
|
||||
"name": "settingsButtons",
|
||||
"orientation": "vertical",
|
||||
"spacing": 10,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 300,
|
||||
"height": 300,
|
||||
"children": [
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "langButton",
|
||||
"x": 1100,
|
||||
"y": 580,
|
||||
"width": 142,
|
||||
"height": 96,
|
||||
"textures": {
|
||||
"normal": "resources/main_menu/lang.png",
|
||||
"hover": "resources/main_menu/lang.png",
|
||||
"pressed": "resources/main_menu/lang.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "titleBtn",
|
||||
"x": 473,
|
||||
"y": 500,
|
||||
"width": 254,
|
||||
"height": 35,
|
||||
"textures": {
|
||||
"normal": "resources/main_menu/title.png",
|
||||
"hover": "resources/main_menu/title.png",
|
||||
"pressed": "resources/main_menu/title.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "underlineBtn",
|
||||
"x": 516,
|
||||
"y": 465,
|
||||
"width": 168,
|
||||
"height": 44,
|
||||
"textures": {
|
||||
"normal": "resources/main_menu/line.png",
|
||||
"hover": "resources/main_menu/line.png",
|
||||
"pressed": "resources/main_menu/line.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "subtitleBtn",
|
||||
"x": 528,
|
||||
"y": 455,
|
||||
"width": 144,
|
||||
"height": 11,
|
||||
"textures": {
|
||||
"normal": "resources/main_menu/subtitle.png",
|
||||
"hover": "resources/main_menu/subtitle.png",
|
||||
"pressed": "resources/main_menu/subtitle.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "singleButton",
|
||||
"x": 409,
|
||||
"y": 360,
|
||||
"width": 382,
|
||||
"height": 56,
|
||||
"textures": {
|
||||
"normal": "resources/main_menu/single.png",
|
||||
"hover": "resources/main_menu/single.png",
|
||||
"pressed": "resources/main_menu/single.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "multiplayerButton",
|
||||
"x": 409,
|
||||
"y": 289,
|
||||
"width": 382,
|
||||
"height": 56,
|
||||
"textures": {
|
||||
"normal": "resources/main_menu/multi.png",
|
||||
"hover": "resources/main_menu/multi.png",
|
||||
"pressed": "resources/main_menu/multi.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "exitButton",
|
||||
"x": 409,
|
||||
"y": 218,
|
||||
"width": 382,
|
||||
"height": 56,
|
||||
"textures": {
|
||||
"normal": "resources/main_menu/exit.png",
|
||||
"hover": "resources/main_menu/exit.png",
|
||||
"pressed": "resources/main_menu/exit.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Button",
|
||||
"name": "versionLabel",
|
||||
"x": 559.5,
|
||||
"y": 170,
|
||||
"width": 81,
|
||||
"height": 9,
|
||||
"textures": {
|
||||
"normal": "resources/main_menu/version.png",
|
||||
"hover": "resources/main_menu/version.png",
|
||||
"pressed": "resources/main_menu/version.png"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
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.
Binary file not shown.
+187
-18
@@ -11,6 +11,8 @@
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <math.h>
|
||||
#include "../src/network/ClientState.h"
|
||||
#include <random>
|
||||
#include <algorithm>
|
||||
|
||||
// Вспомогательный split
|
||||
std::vector<std::string> split(const std::string& s, char delimiter) {
|
||||
@@ -31,6 +33,16 @@ using tcp = net::ip::tcp;
|
||||
|
||||
class Session;
|
||||
|
||||
struct ServerBox {
|
||||
Eigen::Vector3f position;
|
||||
Eigen::Matrix3f rotation;
|
||||
float collisionRadius = 2.0f;
|
||||
};
|
||||
|
||||
std::vector<ServerBox> g_serverBoxes;
|
||||
std::mutex g_boxes_mutex;
|
||||
|
||||
|
||||
std::vector<std::shared_ptr<Session>> g_sessions;
|
||||
std::mutex g_sessions_mutex;
|
||||
|
||||
@@ -38,38 +50,84 @@ 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;
|
||||
|
||||
ClientStateInterval timedClientStates;
|
||||
|
||||
void process_message(const std::string& msg) {
|
||||
auto now_server = std::chrono::system_clock::now();
|
||||
|
||||
auto parts = split(msg, ':');
|
||||
|
||||
if (parts.size() < 16)
|
||||
{
|
||||
throw std::runtime_error("Unknown message type received, too small");
|
||||
if (parts.empty()) {
|
||||
std::cerr << "Empty message received\n";
|
||||
return;
|
||||
}
|
||||
|
||||
std::string type = parts[0];
|
||||
|
||||
if (type == "UPD") {
|
||||
if (parts.size() < 16) {
|
||||
std::cerr << "Invalid UPD message: too few parts (" << parts.size() << ")\n";
|
||||
return;
|
||||
}
|
||||
uint64_t clientTimestamp = std::stoull(parts[1]);
|
||||
|
||||
ClientState receivedState;
|
||||
|
||||
receivedState.id = id_;
|
||||
|
||||
std::chrono::system_clock::time_point uptime_timepoint{ std::chrono::duration_cast<std::chrono::system_clock::time_point::duration>(std::chrono::milliseconds(clientTimestamp)) };
|
||||
std::chrono::system_clock::time_point uptime_timepoint{
|
||||
std::chrono::milliseconds(clientTimestamp)
|
||||
};
|
||||
receivedState.lastUpdateServerTime = uptime_timepoint;
|
||||
|
||||
if (parts[0] == "UPD") {
|
||||
receivedState.handle_full_sync(parts, 2);
|
||||
retranslateMessage(msg);
|
||||
timedClientStates.add_state(receivedState);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Unknown message type received: " + parts[0]);
|
||||
else if (parts[0] == "FIRE") {
|
||||
if (parts.size() < 8) {
|
||||
std::cerr << "Invalid FIRE: too few parts\n";
|
||||
return;
|
||||
}
|
||||
|
||||
timedClientStates.add_state(receivedState);
|
||||
uint64_t clientTime = std::stoull(parts[1]);
|
||||
Eigen::Vector3f pos{
|
||||
std::stof(parts[2]), std::stof(parts[3]), std::stof(parts[4])
|
||||
};
|
||||
Eigen::Vector3f dir{
|
||||
std::stof(parts[5]), std::stof(parts[6]), std::stof(parts[7])
|
||||
};
|
||||
|
||||
int shotCount = 1;
|
||||
if (parts.size() >= 9) {
|
||||
try {
|
||||
shotCount = std::stoi(parts[8]);
|
||||
}
|
||||
catch (...) {
|
||||
shotCount = 1;
|
||||
}
|
||||
}
|
||||
|
||||
std::string broadcast = "PROJECTILE:" +
|
||||
std::to_string(id_) + ":" +
|
||||
std::to_string(clientTime) + ":" +
|
||||
std::to_string(pos.x()) + ":" +
|
||||
std::to_string(pos.y()) + ":" +
|
||||
std::to_string(pos.z()) + ":" +
|
||||
std::to_string(dir.x()) + ":" +
|
||||
std::to_string(dir.y()) + ":" +
|
||||
std::to_string(dir.z()) + ":" +
|
||||
std::to_string(shotCount);
|
||||
|
||||
std::lock_guard<std::mutex> lock(g_sessions_mutex);
|
||||
std::cout << "Player " << id_ << " fired " << shotCount << " shots → broadcasting\n";
|
||||
|
||||
for (auto& session : g_sessions) {
|
||||
if (session->get_id() != id_) {
|
||||
session->send_message(broadcast);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::cerr << "Unknown message type: " << type << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void retranslateMessage(const std::string& msg)
|
||||
@@ -84,6 +142,52 @@ class Session : public std::enable_shared_from_this<Session> {
|
||||
}
|
||||
}
|
||||
|
||||
void sendBoxesToClient() {
|
||||
std::lock_guard<std::mutex> lock(g_boxes_mutex);
|
||||
|
||||
std::string boxMsg = "BOXES:";
|
||||
for (const auto& box : g_serverBoxes) {
|
||||
Eigen::Quaternionf q(box.rotation);
|
||||
boxMsg += std::to_string(box.position.x()) + ":" +
|
||||
std::to_string(box.position.y()) + ":" +
|
||||
std::to_string(box.position.z()) + ":" +
|
||||
std::to_string(q.w()) + ":" +
|
||||
std::to_string(q.x()) + ":" +
|
||||
std::to_string(q.y()) + ":" +
|
||||
std::to_string(q.z()) + "|";
|
||||
}
|
||||
|
||||
if (!boxMsg.empty() && boxMsg.back() == '|') {
|
||||
boxMsg.pop_back();
|
||||
}
|
||||
|
||||
send_message(boxMsg);
|
||||
}
|
||||
|
||||
void send_message(std::string msg) {
|
||||
auto ss = std::make_shared<std::string>(std::move(msg));
|
||||
|
||||
if (is_writing_) {
|
||||
|
||||
ws_.async_write(net::buffer(*ss),
|
||||
[self = shared_from_this(), ss](beast::error_code ec, std::size_t) {
|
||||
if (ec) {
|
||||
std::cerr << "Write error: " << ec.message() << std::endl;
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
is_writing_ = true;
|
||||
ws_.async_write(net::buffer(*ss),
|
||||
[self = shared_from_this(), ss](beast::error_code ec, std::size_t) {
|
||||
self->is_writing_ = false;
|
||||
if (ec) {
|
||||
std::cerr << "Write error: " << ec.message() << std::endl;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
explicit Session(tcp::socket&& socket, int id)
|
||||
@@ -92,6 +196,16 @@ public:
|
||||
|
||||
void init()
|
||||
{
|
||||
sendBoxesToClient();
|
||||
|
||||
auto timer = std::make_shared<net::steady_timer>(ws_.get_executor());
|
||||
timer->expires_after(std::chrono::milliseconds(100));
|
||||
timer->async_wait([self = shared_from_this(), timer](const boost::system::error_code& ec) {
|
||||
if (!ec) {
|
||||
self->send_message("ID:" + std::to_string(self->id_));
|
||||
self->do_read();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void run() {
|
||||
@@ -105,15 +219,15 @@ public:
|
||||
if (ec) return;
|
||||
std::cout << "Client " << self->id_ << " connected\n";
|
||||
self->init();
|
||||
self->send_message("ID:" + std::to_string(self->id_));
|
||||
self->do_read();
|
||||
// self->send_message("ID:" + std::to_string(self->id_));
|
||||
// self->do_read();
|
||||
});
|
||||
}
|
||||
|
||||
void send_message(std::string msg) {
|
||||
/*void send_message(std::string msg) {
|
||||
auto ss = std::make_shared<std::string>(std::move(msg));
|
||||
ws_.async_write(net::buffer(*ss), [ss](beast::error_code, std::size_t) {});
|
||||
}
|
||||
}*/
|
||||
|
||||
int get_id() const {
|
||||
return id_;
|
||||
@@ -151,8 +265,63 @@ void update_world(net::steady_timer& timer, net::io_context& ioc) {
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<ServerBox> generateServerBoxes(int count) {
|
||||
std::vector<ServerBox> boxes;
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
|
||||
const float MIN_COORD = -100.0f;
|
||||
const float MAX_COORD = 100.0f;
|
||||
const float MIN_DISTANCE = 3.0f;
|
||||
const float MIN_DISTANCE_SQUARED = MIN_DISTANCE * MIN_DISTANCE;
|
||||
const int MAX_ATTEMPTS = 1000;
|
||||
|
||||
std::uniform_real_distribution<> posDistrib(MIN_COORD, MAX_COORD);
|
||||
std::uniform_real_distribution<> angleDistrib(0.0, M_PI * 2.0);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
bool accepted = false;
|
||||
int attempts = 0;
|
||||
|
||||
while (!accepted && attempts < MAX_ATTEMPTS) {
|
||||
ServerBox box;
|
||||
box.position = Eigen::Vector3f(
|
||||
(float)posDistrib(gen),
|
||||
(float)posDistrib(gen),
|
||||
(float)posDistrib(gen)
|
||||
);
|
||||
|
||||
accepted = true;
|
||||
for (const auto& existingBox : boxes) {
|
||||
Eigen::Vector3f diff = box.position - existingBox.position;
|
||||
if (diff.squaredNorm() < MIN_DISTANCE_SQUARED) {
|
||||
accepted = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (accepted) {
|
||||
float randomAngle = (float)angleDistrib(gen);
|
||||
Eigen::Vector3f axis = Eigen::Vector3f::Random().normalized();
|
||||
box.rotation = Eigen::AngleAxisf(randomAngle, axis).toRotationMatrix();
|
||||
boxes.push_back(box);
|
||||
}
|
||||
|
||||
attempts++;
|
||||
}
|
||||
}
|
||||
|
||||
return boxes;
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
try {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_boxes_mutex);
|
||||
g_serverBoxes = generateServerBoxes(50);
|
||||
std::cout << "Generated " << g_serverBoxes.size() << " boxes on server\n";
|
||||
}
|
||||
net::io_context ioc;
|
||||
tcp::acceptor acceptor{ ioc, {tcp::v4(), 8080} };
|
||||
int next_id = 0;
|
||||
|
||||
+122
-4
@@ -10,6 +10,7 @@
|
||||
#include <random>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#ifdef __ANDROID__
|
||||
#include <android/log.h>
|
||||
#endif
|
||||
@@ -268,6 +269,10 @@ namespace ZL
|
||||
bool explosionCfgLoaded = explosionEmitter.loadFromJsonFile("resources/config/explosion_config.json", renderer, CONST_ZIP_FILE);
|
||||
explosionEmitter.setEmissionPoints(std::vector<Vector3f>());
|
||||
projectileEmitter.setEmissionPoints(std::vector<Vector3f>());
|
||||
|
||||
uiManager.loadFromFile("resources/config/main_menu.json", renderer, CONST_ZIP_FILE);
|
||||
std::function<void()> loadGameplayUI;
|
||||
loadGameplayUI = [this]() {
|
||||
uiManager.loadFromFile("resources/config/ui.json", renderer, CONST_ZIP_FILE);
|
||||
|
||||
uiManager.startAnimationOnNode("backgroundNode", "bgScroll");
|
||||
@@ -296,10 +301,8 @@ namespace ZL
|
||||
g_exitBgAnimating = false;
|
||||
});
|
||||
|
||||
// Set UI button callbacks
|
||||
uiManager.setButtonCallback("playButton", [this](const std::string& name) {
|
||||
std::cerr << "Play button pressed: " << name << std::endl;
|
||||
|
||||
});
|
||||
|
||||
uiManager.setButtonCallback("settingsButton", [this](const std::string& name) {
|
||||
@@ -332,11 +335,31 @@ namespace ZL
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
uiManager.setButtonCallback("shootButton", [this](const std::string& name) {
|
||||
uint64_t now = SDL_GetTicks64();
|
||||
if (now - lastProjectileFireTime >= static_cast<uint64_t>(projectileCooldownMs)) {
|
||||
lastProjectileFireTime = now;
|
||||
fireProjectiles();
|
||||
|
||||
this->fireProjectiles();
|
||||
|
||||
Eigen::Vector3f localForward = { 0, 0, -1 };
|
||||
Eigen::Vector3f worldForward = (Environment::shipState.rotation * localForward).normalized();
|
||||
|
||||
Eigen::Vector3f centerPos = Environment::shipState.position +
|
||||
Environment::shipState.rotation * Vector3f{ 0, 0.9f, 5.0f };
|
||||
|
||||
std::string fireMsg = "FIRE:" +
|
||||
std::to_string(now) + ":" +
|
||||
std::to_string(centerPos.x()) + ":" +
|
||||
std::to_string(centerPos.y()) + ":" +
|
||||
std::to_string(centerPos.z()) + ":" +
|
||||
std::to_string(worldForward.x()) + ":" +
|
||||
std::to_string(worldForward.y()) + ":" +
|
||||
std::to_string(worldForward.z()) + ":" +
|
||||
"2";
|
||||
|
||||
networkClient->Send(fireMsg);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -346,6 +369,20 @@ namespace ZL
|
||||
newShipVelocity = newVel;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
uiManager.setButtonCallback("singleButton", [loadGameplayUI](const std::string& name) {
|
||||
std::cerr << "Single button pressed: " << name << " -> load gameplay UI\n";
|
||||
loadGameplayUI();
|
||||
});
|
||||
uiManager.setButtonCallback("multiplayerButton", [loadGameplayUI](const std::string& name) {
|
||||
std::cerr << "Multiplayer button pressed: " << name << " -> load gameplay UI\n";
|
||||
loadGameplayUI();
|
||||
});
|
||||
uiManager.setButtonCallback("exitButton", [](const std::string& name) {
|
||||
std::cerr << "Exit from main menu pressed: " << name << " -> exiting\n";
|
||||
Environment::exitGameLoop = true;
|
||||
});
|
||||
|
||||
cubemapTexture = std::make_shared<Texture>(
|
||||
std::array<TextureDataStruct, 6>{
|
||||
@@ -692,6 +729,27 @@ namespace ZL
|
||||
|
||||
latestRemotePlayers = networkClient->getRemotePlayers();
|
||||
|
||||
// Если сервер прислал коробки, применяем их однократно вместо локальной генерации
|
||||
if (!serverBoxesApplied && networkClient) {
|
||||
auto sboxes = networkClient->getServerBoxes();
|
||||
if (!sboxes.empty()) {
|
||||
boxCoordsArr.clear();
|
||||
for (auto& b : sboxes) {
|
||||
BoxCoords bc;
|
||||
bc.pos = b.first;
|
||||
bc.m = b.second;
|
||||
boxCoordsArr.push_back(bc);
|
||||
}
|
||||
boxRenderArr.resize(boxCoordsArr.size());
|
||||
for (int i = 0; i < (int)boxCoordsArr.size(); ++i) {
|
||||
boxRenderArr[i].AssignFrom(boxBase);
|
||||
boxRenderArr[i].RefreshVBO();
|
||||
}
|
||||
boxAlive.assign(boxCoordsArr.size(), true);
|
||||
serverBoxesApplied = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Итерируемся по актуальным данным из extrapolateRemotePlayers
|
||||
for (auto const& [id, remotePlayer] : latestRemotePlayers) {
|
||||
|
||||
@@ -770,7 +828,16 @@ namespace ZL
|
||||
continue;
|
||||
|
||||
float uiX = sx, uiY = sy;
|
||||
float scale = std::clamp(BASE_SCALE / (dist * PERSPECTIVE_K + 1.f), MIN_SCALE, MAX_SCALE);
|
||||
//float scale = std::clamp(BASE_SCALE / (dist * PERSPECTIVE_K + 1.f), MIN_SCALE, MAX_SCALE);
|
||||
float scale;
|
||||
if (dist > CLOSE_DIST) {
|
||||
scale = 0.4f;
|
||||
}
|
||||
else {
|
||||
float t = 1.0f - (dist / CLOSE_DIST);
|
||||
scale = 0.4f + (MAX_SCALE - 0.4f) * t;
|
||||
}
|
||||
scale = std::clamp(scale, MIN_SCALE, MAX_SCALE);
|
||||
|
||||
// Дефолтный лейбл
|
||||
std::string label = "Player (" + std::to_string(st.id) + ") " + std::to_string((int)dist) + "m";
|
||||
@@ -1255,6 +1322,57 @@ namespace ZL
|
||||
render();
|
||||
mainThreadHandler.processMainThreadTasks();
|
||||
networkClient->Poll();
|
||||
|
||||
if (networkClient) {
|
||||
auto pending = networkClient->getPendingProjectiles();
|
||||
if (!pending.empty()) {
|
||||
const float projectileSpeed = 60.0f;
|
||||
const float lifeMs = 5000.0f;
|
||||
const float size = 0.5f;
|
||||
|
||||
auto remotePlayersSnapshot = networkClient->getRemotePlayers();
|
||||
for (const auto& pi : pending) {
|
||||
Eigen::Vector3f dir = pi.direction;
|
||||
float len = dir.norm();
|
||||
if (len <= 1e-6f) continue;
|
||||
dir /= len;
|
||||
|
||||
Eigen::Matrix3f shooterRot = Eigen::Matrix3f::Identity();
|
||||
float shooterVel = 0.0f;
|
||||
auto it = remotePlayersSnapshot.find(pi.shooterId);
|
||||
if (it != remotePlayersSnapshot.end()) {
|
||||
std::chrono::system_clock::time_point pktTime{ std::chrono::milliseconds(pi.clientTime) };
|
||||
if (it->second.canFetchClientStateAtTime(pktTime)) {
|
||||
ClientState shooterState = it->second.fetchClientStateAtTime(pktTime);
|
||||
shooterRot = shooterState.rotation;
|
||||
shooterVel = shooterState.velocity;
|
||||
}
|
||||
}
|
||||
|
||||
float speedWithOwner = projectileSpeed + shooterVel;
|
||||
Eigen::Vector3f baseVel = dir * speedWithOwner;
|
||||
|
||||
int shotCount = 2;
|
||||
|
||||
std::vector<Eigen::Vector3f> localOffsets = {
|
||||
{-1.5f, 0.9f, 5.0f},
|
||||
{ 1.5f, 0.9f, 5.0f}
|
||||
};
|
||||
|
||||
for (int i = 0; i < shotCount; ++i) {
|
||||
Eigen::Vector3f rotatedOffset = shooterRot * localOffsets[i];
|
||||
Eigen::Vector3f shotPos = pi.position + rotatedOffset;
|
||||
|
||||
for (auto& p : projectiles) {
|
||||
if (!p->isActive()) {
|
||||
p->init(shotPos, baseVel, lifeMs, size, projectileTexture, renderer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Game::handleDown(int mx, int my)
|
||||
|
||||
+3
-1
@@ -121,7 +121,9 @@ namespace ZL {
|
||||
static constexpr float BASE_SCALE = 140.f;
|
||||
static constexpr float PERSPECTIVE_K = 0.05f; // Tune
|
||||
static constexpr float MIN_SCALE = 0.4f;
|
||||
static constexpr float MAX_SCALE = 1.5f;
|
||||
static constexpr float MAX_SCALE = 0.8f;
|
||||
static constexpr float CLOSE_DIST = 600.0f;
|
||||
bool serverBoxesApplied = false;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ constexpr float SHIP_ACCEL = 1.0f * 1000.0f;
|
||||
constexpr float ROTATION_SENSITIVITY = 0.002f;
|
||||
|
||||
constexpr long long SERVER_DELAY = 0; //ms
|
||||
constexpr long long CLIENT_DELAY = 200; //ms
|
||||
constexpr long long CLIENT_DELAY = 1000; //ms
|
||||
constexpr long long CUTOFF_TIME = 5000; //ms
|
||||
|
||||
struct ClientState {
|
||||
|
||||
@@ -11,8 +11,10 @@ namespace ZL {
|
||||
}
|
||||
|
||||
void LocalClient::Send(const std::string& message) {
|
||||
|
||||
}
|
||||
|
||||
std::vector<ProjectileInfo> LocalClient::getPendingProjectiles() {
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,9 +17,14 @@ namespace ZL {
|
||||
|
||||
bool IsConnected() const override { return true; }
|
||||
int GetClientId() const { return 1; }
|
||||
std::vector<ProjectileInfo> getPendingProjectiles();
|
||||
|
||||
std::unordered_map<int, ClientStateInterval> getRemotePlayers() override {
|
||||
return std::unordered_map<int, ClientStateInterval>();
|
||||
}
|
||||
|
||||
std::vector<std::pair<Eigen::Vector3f, Eigen::Matrix3f>> getServerBoxes() override {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -7,6 +7,13 @@
|
||||
// NetworkInterface.h - »нтерфейс дл¤ разных типов соединений
|
||||
namespace ZL {
|
||||
|
||||
struct ProjectileInfo {
|
||||
int shooterId = -1;
|
||||
uint64_t clientTime = 0;
|
||||
Eigen::Vector3f position = Eigen::Vector3f::Zero();
|
||||
Eigen::Vector3f direction = Eigen::Vector3f::Zero();
|
||||
};
|
||||
|
||||
class INetworkClient {
|
||||
public:
|
||||
virtual ~INetworkClient() = default;
|
||||
@@ -15,5 +22,9 @@ namespace ZL {
|
||||
virtual bool IsConnected() const = 0;
|
||||
virtual void Poll() = 0; // ƒл¤ обработки вход¤щих пакетов
|
||||
virtual std::unordered_map<int, ClientStateInterval> getRemotePlayers() = 0;
|
||||
|
||||
virtual std::vector<std::pair<Eigen::Vector3f, Eigen::Matrix3f>> getServerBoxes() = 0;
|
||||
|
||||
virtual std::vector<ProjectileInfo> getPendingProjectiles() = 0;
|
||||
};
|
||||
}
|
||||
@@ -64,6 +64,13 @@ namespace ZL {
|
||||
messageQueue.push(msg);
|
||||
}
|
||||
|
||||
std::vector<ProjectileInfo> WebSocketClient::getPendingProjectiles() {
|
||||
std::lock_guard<std::mutex> lock(projMutex_);
|
||||
auto copy = pendingProjectiles_;
|
||||
pendingProjectiles_.clear();
|
||||
return copy;
|
||||
}
|
||||
|
||||
void WebSocketClient::Poll() {
|
||||
std::lock_guard<std::mutex> lock(queueMutex);
|
||||
|
||||
@@ -83,6 +90,68 @@ namespace ZL {
|
||||
std::string msg = messageQueue.front();
|
||||
messageQueue.pop();
|
||||
|
||||
// Обработка списка коробок от сервера
|
||||
if (msg.rfind("BOXES:", 0) == 0) {
|
||||
std::string payload = msg.substr(6); // после "BOXES:"
|
||||
std::vector<std::pair<Eigen::Vector3f, Eigen::Matrix3f>> parsedBoxes;
|
||||
if (!payload.empty()) {
|
||||
auto items = split(payload, '|');
|
||||
for (auto& item : items) {
|
||||
if (item.empty()) continue;
|
||||
auto parts = split(item, ':');
|
||||
if (parts.size() < 7) continue;
|
||||
try {
|
||||
float px = std::stof(parts[0]);
|
||||
float py = std::stof(parts[1]);
|
||||
float pz = std::stof(parts[2]);
|
||||
Eigen::Quaternionf q(
|
||||
std::stof(parts[3]),
|
||||
std::stof(parts[4]),
|
||||
std::stof(parts[5]),
|
||||
std::stof(parts[6])
|
||||
);
|
||||
Eigen::Matrix3f rot = q.toRotationMatrix();
|
||||
parsedBoxes.emplace_back(Eigen::Vector3f{ px, py, pz }, rot);
|
||||
}
|
||||
catch (...) {
|
||||
// пропускаем некорректную запись
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> bLock(boxesMutex);
|
||||
serverBoxes_ = std::move(parsedBoxes);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (msg.rfind("PROJECTILE:", 0) == 0) {
|
||||
auto parts = split(msg, ':');
|
||||
if (parts.size() >= 9) {
|
||||
try {
|
||||
ProjectileInfo pi;
|
||||
pi.shooterId = std::stoi(parts[1]);
|
||||
pi.clientTime = std::stoull(parts[2]);
|
||||
pi.position = Eigen::Vector3f(
|
||||
std::stof(parts[3]),
|
||||
std::stof(parts[4]),
|
||||
std::stof(parts[5])
|
||||
);
|
||||
pi.direction = Eigen::Vector3f(
|
||||
std::stof(parts[6]),
|
||||
std::stof(parts[7]),
|
||||
std::stof(parts[8])
|
||||
);
|
||||
std::lock_guard<std::mutex> pl(projMutex_);
|
||||
pendingProjectiles_.push_back(pi);
|
||||
}
|
||||
catch (...) {
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (msg.rfind("EVENT:", 0) == 0) {
|
||||
auto parts = split(msg, ':');
|
||||
if (parts.size() < 5) continue; // EVENT:ID:TYPE:TIME:DATA...
|
||||
|
||||
@@ -34,6 +34,13 @@ namespace ZL {
|
||||
std::unordered_map<int, ClientStateInterval> remotePlayers;
|
||||
std::mutex playersMutex;
|
||||
|
||||
// Серверные коробки
|
||||
std::vector<std::pair<Eigen::Vector3f, Eigen::Matrix3f>> serverBoxes_;
|
||||
std::mutex boxesMutex;
|
||||
|
||||
std::vector<ProjectileInfo> pendingProjectiles_;
|
||||
std::mutex projMutex_;
|
||||
|
||||
void startAsyncRead();
|
||||
void processIncomingMessage(const std::string& msg);
|
||||
|
||||
@@ -54,6 +61,13 @@ namespace ZL {
|
||||
std::lock_guard<std::mutex> lock(playersMutex);
|
||||
return remotePlayers;
|
||||
}
|
||||
|
||||
std::vector<std::pair<Eigen::Vector3f, Eigen::Matrix3f>> getServerBoxes() override {
|
||||
std::lock_guard<std::mutex> lock(boxesMutex);
|
||||
return serverBoxes_;
|
||||
}
|
||||
|
||||
std::vector<ProjectileInfo> getPendingProjectiles() override;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
Reference in new issue
Block a user