add local server
This commit is contained in:
parent
a59bcc0c4b
commit
629c9ba7b1
BIN
resources/Cargo_Base_color_sRGB.png
(Stored with Git LFS)
Normal file
BIN
resources/Cargo_Base_color_sRGB.png
(Stored with Git LFS)
Normal file
Binary file not shown.
6175
resources/cargoship001.txt
Normal file
6175
resources/cargoship001.txt
Normal file
File diff suppressed because it is too large
Load Diff
32
src/Game.cpp
32
src/Game.cpp
@ -26,8 +26,8 @@ namespace ZL
|
||||
#ifdef EMSCRIPTEN
|
||||
const char* CONST_ZIP_FILE = "resources.zip";
|
||||
#else
|
||||
const char* CONST_ZIP_FILE = "C:\\Work\\Projects\\space-game001\\resources.zip";
|
||||
//const char* CONST_ZIP_FILE = "";
|
||||
//const char* CONST_ZIP_FILE = "C:\\Work\\Projects\\space-game001\\resources.zip";
|
||||
const char* CONST_ZIP_FILE = "";
|
||||
#endif
|
||||
|
||||
static bool g_exitBgAnimating = false;
|
||||
@ -440,12 +440,27 @@ namespace ZL
|
||||
//spaceshipBase = LoadFromTextFile02("./resources/spaceship006x.txt", CONST_ZIP_FILE);
|
||||
//spaceshipBase.RotateByMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(-M_PI / 2.0, Eigen::Vector3f::UnitY())).toRotationMatrix());// QuatFromRotateAroundY(M_PI / 2.0).toRotationMatrix());
|
||||
|
||||
|
||||
spaceshipTexture = std::make_unique<Texture>(CreateTextureDataFromPng("resources/MainCharacter_Base_color_sRGB.png", 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.Move(Vector3f{ 1.2, 0, -5 });
|
||||
|
||||
/*
|
||||
spaceshipTexture = std::make_unique<Texture>(CreateTextureDataFromPng("resources/Cargo_Base_color_sRGB.png", CONST_ZIP_FILE));
|
||||
spaceshipBase = LoadFromTextFile02("resources/cargoship001.txt", CONST_ZIP_FILE);
|
||||
|
||||
auto quat = Eigen::Quaternionf(Eigen::AngleAxisf(-M_PI*0.5, Eigen::Vector3f::UnitZ()));
|
||||
auto rotMatrix = quat.toRotationMatrix();
|
||||
spaceshipBase.RotateByMatrix(rotMatrix);
|
||||
|
||||
auto quat2 = Eigen::Quaternionf(Eigen::AngleAxisf(M_PI*0.5, Eigen::Vector3f::UnitY()));
|
||||
auto rotMatrix2 = quat2.toRotationMatrix();
|
||||
spaceshipBase.RotateByMatrix(rotMatrix2);
|
||||
|
||||
//spaceshipBase.RotateByMatrix(Eigen::Quaternionf(Eigen::AngleAxisf(M_PI, Eigen::Vector3f::UnitY())).toRotationMatrix());// QuatFromRotateAroundY(M_PI / 2.0).toRotationMatrix());
|
||||
*/
|
||||
spaceship.AssignFrom(spaceshipBase);
|
||||
spaceship.RefreshVBO();
|
||||
|
||||
@ -835,7 +850,7 @@ namespace ZL
|
||||
for (auto const& [id, remotePlayer] : remotePlayerStates)
|
||||
{
|
||||
|
||||
//<<<<<<< HEAD
|
||||
//<<<<<<< HEAD
|
||||
const ClientState& st = remotePlayer;
|
||||
// Позиция корабля в мире
|
||||
Vector3f shipWorld = st.position;
|
||||
@ -1391,8 +1406,17 @@ namespace ZL
|
||||
#endif
|
||||
}
|
||||
render();
|
||||
mainThreadHandler.processMainThreadTasks();
|
||||
|
||||
if (networkClient) {
|
||||
#ifndef NETWORK
|
||||
auto localClient = dynamic_cast<ZL::LocalClient*>(networkClient.get());
|
||||
if (localClient) {
|
||||
localClient->setLocalPlayerState(Environment::shipState);
|
||||
}
|
||||
#endif
|
||||
networkClient->Poll();
|
||||
}
|
||||
mainThreadHandler.processMainThreadTasks();
|
||||
|
||||
if (networkClient) {
|
||||
auto pending = networkClient->getPendingProjectiles();
|
||||
|
||||
@ -1,20 +1,288 @@
|
||||
#include "LocalClient.h"
|
||||
#include <iostream>
|
||||
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <math.h>
|
||||
|
||||
namespace ZL {
|
||||
|
||||
void LocalClient::Connect(const std::string& host, uint16_t port) {
|
||||
generateBoxes();
|
||||
lastUpdateMs = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
}
|
||||
|
||||
void LocalClient::generateBoxes() {
|
||||
serverBoxes.clear();
|
||||
|
||||
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 < 50; i++) {
|
||||
bool accepted = false;
|
||||
int attempts = 0;
|
||||
|
||||
while (!accepted && attempts < MAX_ATTEMPTS) {
|
||||
LocalServerBox box;
|
||||
box.position = Eigen::Vector3f(
|
||||
(float)posDistrib(gen),
|
||||
(float)posDistrib(gen),
|
||||
(float)posDistrib(gen)
|
||||
);
|
||||
|
||||
accepted = true;
|
||||
for (const auto& existingBox : serverBoxes) {
|
||||
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();
|
||||
serverBoxes.push_back(box);
|
||||
}
|
||||
|
||||
attempts++;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "LocalClient: Generated " << serverBoxes.size() << " boxes\n";
|
||||
}
|
||||
|
||||
void LocalClient::Poll() {
|
||||
updatePhysics();
|
||||
checkCollisions();
|
||||
}
|
||||
|
||||
void LocalClient::updatePhysics() {
|
||||
auto now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
|
||||
if (lastUpdateMs == 0) {
|
||||
lastUpdateMs = now_ms;
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t deltaMs = now_ms - lastUpdateMs;
|
||||
float dt = deltaMs / 1000.0f;
|
||||
lastUpdateMs = now_ms;
|
||||
|
||||
std::vector<int> indicesToRemove;
|
||||
|
||||
for (size_t i = 0; i < projectiles.size(); ++i) {
|
||||
auto& pr = projectiles[i];
|
||||
pr.pos += pr.vel * dt;
|
||||
|
||||
if (now_ms > pr.spawnMs + static_cast<uint64_t>(pr.lifeMs)) {
|
||||
indicesToRemove.push_back(static_cast<int>(i));
|
||||
}
|
||||
}
|
||||
|
||||
if (!indicesToRemove.empty()) {
|
||||
std::sort(indicesToRemove.rbegin(), indicesToRemove.rend());
|
||||
for (int idx : indicesToRemove) {
|
||||
if (idx >= 0 && idx < (int)projectiles.size()) {
|
||||
projectiles.erase(projectiles.begin() + idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LocalClient::checkCollisions() {
|
||||
auto now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
|
||||
const float projectileHitRadius = 1.5f;
|
||||
const float boxCollisionRadius = 2.0f;
|
||||
const float shipCollisionRadius = 15.0f;
|
||||
|
||||
std::vector<std::pair<size_t, size_t>> boxProjectileCollisions;
|
||||
|
||||
for (size_t bi = 0; bi < serverBoxes.size(); ++bi) {
|
||||
if (serverBoxes[bi].destroyed) continue;
|
||||
|
||||
Eigen::Vector3f boxWorld = serverBoxes[bi].position + Eigen::Vector3f(0.0f, 6.0f, 45000.0f);
|
||||
|
||||
for (size_t pi = 0; pi < projectiles.size(); ++pi) {
|
||||
const auto& pr = projectiles[pi];
|
||||
Eigen::Vector3f diff = pr.pos - boxWorld;
|
||||
float thresh = boxCollisionRadius + projectileHitRadius;
|
||||
|
||||
if (diff.squaredNorm() <= thresh * thresh) {
|
||||
boxProjectileCollisions.push_back({ bi, pi });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int> projIndicesToRemove;
|
||||
for (const auto& [boxIdx, projIdx] : boxProjectileCollisions) {
|
||||
if (!serverBoxes[boxIdx].destroyed) {
|
||||
serverBoxes[boxIdx].destroyed = true;
|
||||
|
||||
Eigen::Vector3f boxWorld = serverBoxes[boxIdx].position + Eigen::Vector3f(0.0f, 0.0f, 45000.0f);
|
||||
|
||||
BoxDestroyedInfo destruction;
|
||||
destruction.boxIndex = static_cast<int>(boxIdx);
|
||||
destruction.serverTime = now_ms;
|
||||
destruction.position = boxWorld;
|
||||
destruction.destroyedBy = projectiles[projIdx].shooterId;
|
||||
|
||||
pendingBoxDestructions.push_back(destruction);
|
||||
|
||||
std::cout << "LocalClient: Box " << boxIdx << " destroyed by projectile from player "
|
||||
<< projectiles[projIdx].shooterId << std::endl;
|
||||
|
||||
if (std::find(projIndicesToRemove.begin(), projIndicesToRemove.end(), (int)projIdx)
|
||||
== projIndicesToRemove.end()) {
|
||||
projIndicesToRemove.push_back(static_cast<int>(projIdx));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!projIndicesToRemove.empty()) {
|
||||
std::sort(projIndicesToRemove.rbegin(), projIndicesToRemove.rend());
|
||||
for (int idx : projIndicesToRemove) {
|
||||
if (idx >= 0 && idx < (int)projectiles.size()) {
|
||||
projectiles.erase(projectiles.begin() + idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasLocalPlayerState) {
|
||||
for (size_t bi = 0; bi < serverBoxes.size(); ++bi) {
|
||||
if (serverBoxes[bi].destroyed) continue;
|
||||
|
||||
Eigen::Vector3f boxWorld = serverBoxes[bi].position + Eigen::Vector3f(0.0f, 0.0f, 45000.0f);
|
||||
Eigen::Vector3f diff = localPlayerState.position - boxWorld;
|
||||
float thresh = shipCollisionRadius + boxCollisionRadius;
|
||||
|
||||
if (diff.squaredNorm() <= thresh * thresh) {
|
||||
serverBoxes[bi].destroyed = true;
|
||||
|
||||
BoxDestroyedInfo destruction;
|
||||
destruction.boxIndex = static_cast<int>(bi);
|
||||
destruction.serverTime = now_ms;
|
||||
destruction.position = boxWorld;
|
||||
destruction.destroyedBy = GetClientId();
|
||||
|
||||
pendingBoxDestructions.push_back(destruction);
|
||||
|
||||
std::cout << "LocalClient: Box " << bi << " destroyed by ship collision with player "
|
||||
<< GetClientId() << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LocalClient::Send(const std::string& message) {
|
||||
auto parts = [](const std::string& s, char delimiter) {
|
||||
std::vector<std::string> tokens;
|
||||
std::string token;
|
||||
std::istringstream tokenStream(s);
|
||||
while (std::getline(tokenStream, token, delimiter)) {
|
||||
tokens.push_back(token);
|
||||
}
|
||||
return tokens;
|
||||
}(message, ':');
|
||||
|
||||
if (parts.empty()) return;
|
||||
|
||||
std::string type = parts[0];
|
||||
|
||||
if (type == "FIRE") {
|
||||
if (parts.size() < 10) return;
|
||||
|
||||
uint64_t clientTime = std::stoull(parts[1]);
|
||||
Eigen::Vector3f pos{
|
||||
std::stof(parts[2]), std::stof(parts[3]), std::stof(parts[4])
|
||||
};
|
||||
Eigen::Quaternionf dir(
|
||||
std::stof(parts[5]), std::stof(parts[6]), std::stof(parts[7]), std::stof(parts[8])
|
||||
);
|
||||
float velocity = std::stof(parts[9]);
|
||||
|
||||
int shotCount = 2;
|
||||
if (parts.size() >= 11) {
|
||||
try { shotCount = std::stoi(parts[10]); }
|
||||
catch (...) { shotCount = 2; }
|
||||
}
|
||||
|
||||
const std::vector<Eigen::Vector3f> localOffsets = {
|
||||
Eigen::Vector3f(-1.5f, 0.9f, 5.0f),
|
||||
Eigen::Vector3f(1.5f, 0.9f, 5.0f)
|
||||
};
|
||||
|
||||
uint64_t now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
|
||||
for (int i = 0; i < std::min(shotCount, (int)localOffsets.size()); ++i) {
|
||||
LocalProjectile pr;
|
||||
pr.shooterId = GetClientId();
|
||||
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 = 5000.0f;
|
||||
projectiles.push_back(pr);
|
||||
|
||||
ProjectileInfo pinfo;
|
||||
pinfo.shooterId = pr.shooterId;
|
||||
pinfo.clientTime = clientTime;
|
||||
pinfo.position = pr.pos;
|
||||
pinfo.rotation = dir.toRotationMatrix();
|
||||
pinfo.velocity = velocity;
|
||||
pendingProjectiles.push_back(pinfo);
|
||||
|
||||
std::cout << "LocalClient: Created projectile at pos (" << shotPos.x() << ", "
|
||||
<< shotPos.y() << ", " << shotPos.z() << ") vel (" << pr.vel.x() << ", "
|
||||
<< pr.vel.y() << ", " << pr.vel.z() << ")" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ProjectileInfo> LocalClient::getPendingProjectiles() {
|
||||
return {};
|
||||
auto result = pendingProjectiles;
|
||||
pendingProjectiles.clear();
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::pair<Eigen::Vector3f, Eigen::Matrix3f>> LocalClient::getServerBoxes() {
|
||||
std::vector<std::pair<Eigen::Vector3f, Eigen::Matrix3f>> result;
|
||||
for (const auto& box : serverBoxes) {
|
||||
result.push_back({ box.position, box.rotation });
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<DeathInfo> LocalClient::getPendingDeaths() {
|
||||
auto result = pendingDeaths;
|
||||
pendingDeaths.clear();
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<BoxDestroyedInfo> LocalClient::getPendingBoxDestructions() {
|
||||
auto result = pendingBoxDestructions;
|
||||
pendingBoxDestructions.clear();
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@ -2,11 +2,46 @@
|
||||
|
||||
#include "NetworkInterface.h"
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
#include <Eigen/Dense>
|
||||
#include <chrono>
|
||||
#include <random>
|
||||
|
||||
namespace ZL {
|
||||
|
||||
struct LocalServerBox {
|
||||
Eigen::Vector3f position;
|
||||
Eigen::Matrix3f rotation;
|
||||
float collisionRadius = 2.0f;
|
||||
bool destroyed = false;
|
||||
};
|
||||
|
||||
struct LocalProjectile {
|
||||
int shooterId = -1;
|
||||
uint64_t spawnMs = 0;
|
||||
Eigen::Vector3f pos;
|
||||
Eigen::Vector3f vel;
|
||||
float lifeMs = 5000.0f;
|
||||
};
|
||||
|
||||
class LocalClient : public INetworkClient {
|
||||
private:
|
||||
std::queue<std::string> messageQueue;
|
||||
std::vector<LocalServerBox> serverBoxes;
|
||||
std::vector<LocalProjectile> projectiles;
|
||||
std::vector<ProjectileInfo> pendingProjectiles;
|
||||
std::vector<DeathInfo> pendingDeaths;
|
||||
std::vector<BoxDestroyedInfo> pendingBoxDestructions;
|
||||
std::vector<int> pendingRespawns;
|
||||
|
||||
uint64_t lastUpdateMs = 0;
|
||||
ClientState localPlayerState;
|
||||
bool hasLocalPlayerState = false;
|
||||
|
||||
void updatePhysics();
|
||||
void checkCollisions();
|
||||
void generateBoxes();
|
||||
|
||||
public:
|
||||
void Connect(const std::string& host, uint16_t port) override;
|
||||
|
||||
@ -22,21 +57,19 @@ namespace ZL {
|
||||
return std::unordered_map<int, ClientStateInterval>();
|
||||
}
|
||||
|
||||
std::vector<std::pair<Eigen::Vector3f, Eigen::Matrix3f>> getServerBoxes() override {
|
||||
return {};
|
||||
}
|
||||
std::vector<std::pair<Eigen::Vector3f, Eigen::Matrix3f>> getServerBoxes() override;
|
||||
|
||||
std::vector<DeathInfo> getPendingDeaths() override {
|
||||
return {};
|
||||
}
|
||||
std::vector<DeathInfo> getPendingDeaths() override;
|
||||
|
||||
std::vector<int> getPendingRespawns() override {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<BoxDestroyedInfo> getPendingBoxDestructions() override
|
||||
{
|
||||
return {};
|
||||
std::vector<BoxDestroyedInfo> getPendingBoxDestructions() override;
|
||||
|
||||
void setLocalPlayerState(const ClientState& state) {
|
||||
localPlayerState = state;
|
||||
hasLocalPlayerState = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user