added sync projectiles/boxes

This commit is contained in:
Vlad 2026-01-27 19:38:18 +06:00
parent cacc18dc7e
commit e8fb14b809
7 changed files with 338 additions and 192 deletions

View File

@ -56,33 +56,76 @@ class Session : public std::enable_shared_from_this<Session> {
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) {
session->send_message(broadcast);
}
}
else {
std::cerr << "Unknown message type: " << type << "\n";
}
}
void retranslateMessage(const std::string& msg)

View File

@ -232,7 +232,24 @@ namespace ZL
uint64_t now = SDL_GetTicks64();
if (now - lastProjectileFireTime >= static_cast<uint64_t>(projectileCooldownMs)) {
lastProjectileFireTime = now;
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);
}
});
@ -1105,6 +1122,42 @@ 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;
for (const auto& pi : pending) {
Eigen::Vector3f dir = pi.direction;
float len = dir.norm();
if (len <= 1e-6f) continue;
dir /= len;
Eigen::Vector3f baseVel = dir * projectileSpeed;
int shotCount = 1;
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 shotPos = pi.position + localOffsets[i];
for (auto& p : projectiles) {
if (!p->isActive()) {
p->init(shotPos, baseVel, lifeMs, size, projectileTexture, renderer);
break;
}
}
}
}
}
}
}
void Game::handleDown(int mx, int my)

View File

@ -11,8 +11,10 @@ namespace ZL {
}
void LocalClient::Send(const std::string& message) {
}
std::vector<ProjectileInfo> LocalClient::getPendingProjectiles() {
return {};
}
}

View File

@ -17,6 +17,7 @@ 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>();

View File

@ -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;
@ -17,5 +24,7 @@ namespace ZL {
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;
};
}

View File

@ -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);
@ -119,6 +126,32 @@ namespace ZL {
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...

View File

@ -38,6 +38,9 @@ namespace ZL {
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);
@ -63,6 +66,8 @@ namespace ZL {
std::lock_guard<std::mutex> lock(boxesMutex);
return serverBoxes_;
}
std::vector<ProjectileInfo> getPendingProjectiles() override;
};
}
#endif