second commit
This commit is contained in:
@@ -38,6 +38,35 @@ namespace {
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
constexpr uint32_t NET_MAGIC_BLAST = 0x45494942; // "EIIB"
|
||||
constexpr int BLAST_COPIES = 4; // each blast is sent this many times
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct BlastPacket {
|
||||
uint32_t magic;
|
||||
uint16_t version;
|
||||
uint8_t sender;
|
||||
uint32_t id;
|
||||
float center[3];
|
||||
float radius;
|
||||
float power;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
constexpr uint32_t NET_MAGIC_HIT = 0x45494948; // "EIIH"
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct HitPacket {
|
||||
uint32_t magic;
|
||||
uint16_t version;
|
||||
uint8_t sender;
|
||||
uint8_t target;
|
||||
uint32_t id;
|
||||
int16_t damage;
|
||||
float knock[3];
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
constexpr int MAX_ENTRIES = Lan::MaxPlayers;
|
||||
constexpr int MAX_DATAGRAM = (int)sizeof(Header) + MAX_ENTRIES * (int)sizeof(Entry);
|
||||
|
||||
@@ -92,6 +121,33 @@ struct Lan::Impl {
|
||||
uint32_t lastSeq = 0;
|
||||
double lastRecvTime = 0.0;
|
||||
|
||||
// Blasts waiting for their remaining copies to go out
|
||||
struct PendingBlast { BlastPacket pkt; int copiesLeft; };
|
||||
std::vector<PendingBlast> pendingBlasts;
|
||||
uint32_t blastSeq = 0;
|
||||
std::vector<uint64_t> seenBlasts; // (type, sender, id) of recent blasts/hits, for de-duplication
|
||||
|
||||
struct PendingHit { HitPacket pkt; int copiesLeft; };
|
||||
std::vector<PendingHit> pendingHits;
|
||||
uint32_t hitSeq = 0;
|
||||
|
||||
void SendHitPacket(const sockaddr_in& to, const HitPacket& pkt) {
|
||||
sendto(sock, (const char*)&pkt, (int)sizeof(pkt), 0, (const sockaddr*)&to, (int)sizeof(to));
|
||||
}
|
||||
|
||||
// True the first time a (type, sender, id) triple is seen. type: 0 = blast, 1 = hit.
|
||||
bool FirstSighting(uint8_t type, uint8_t sender, uint32_t id) {
|
||||
const uint64_t key = ((uint64_t)type << 40) | ((uint64_t)sender << 32) | id;
|
||||
if (std::find(seenBlasts.begin(), seenBlasts.end(), key) != seenBlasts.end()) return false;
|
||||
seenBlasts.push_back(key);
|
||||
if (seenBlasts.size() > 64) seenBlasts.erase(seenBlasts.begin());
|
||||
return true;
|
||||
}
|
||||
|
||||
void SendBlastPacket(const sockaddr_in& to, const BlastPacket& pkt) {
|
||||
sendto(sock, (const char*)&pkt, (int)sizeof(pkt), 0, (const sockaddr*)&to, (int)sizeof(to));
|
||||
}
|
||||
|
||||
void SendDatagram(const sockaddr_in& to, const std::vector<Entry>& entries) {
|
||||
char buf[MAX_DATAGRAM];
|
||||
Header h;
|
||||
@@ -174,14 +230,68 @@ void Lan::Stop() {
|
||||
impl->wsaStarted = false;
|
||||
}
|
||||
impl->peers.clear();
|
||||
impl->pendingBlasts.clear();
|
||||
impl->pendingHits.clear();
|
||||
impl->seenBlasts.clear();
|
||||
impl->anyRecv = false;
|
||||
active = false;
|
||||
mode = Mode::Off;
|
||||
}
|
||||
|
||||
void Lan::SendBlast(const NetBlast& b) {
|
||||
if (!active) return;
|
||||
Impl::PendingBlast pb;
|
||||
pb.pkt.magic = NET_MAGIC_BLAST;
|
||||
pb.pkt.version = NET_VERSION;
|
||||
pb.pkt.sender = 0; // the host stamps the real id when relaying a client's blast
|
||||
pb.pkt.id = ++impl->blastSeq;
|
||||
pb.pkt.center[0] = b.center.x; pb.pkt.center[1] = b.center.y; pb.pkt.center[2] = b.center.z;
|
||||
pb.pkt.radius = b.radius;
|
||||
pb.pkt.power = b.power;
|
||||
pb.copiesLeft = BLAST_COPIES;
|
||||
impl->pendingBlasts.push_back(pb);
|
||||
}
|
||||
|
||||
void Lan::SendHit(uint8_t target, const NetHit& h) {
|
||||
if (!active) return;
|
||||
Impl::PendingHit ph;
|
||||
ph.pkt.magic = NET_MAGIC_HIT;
|
||||
ph.pkt.version = NET_VERSION;
|
||||
ph.pkt.sender = 0; // the host stamps the real id when relaying a client's hit
|
||||
ph.pkt.target = target;
|
||||
ph.pkt.id = ++impl->hitSeq;
|
||||
ph.pkt.damage = (int16_t)std::clamp(h.damage, 0, 1000);
|
||||
ph.pkt.knock[0] = h.knockback.x; ph.pkt.knock[1] = h.knockback.y; ph.pkt.knock[2] = h.knockback.z;
|
||||
ph.copiesLeft = BLAST_COPIES;
|
||||
impl->pendingHits.push_back(ph);
|
||||
}
|
||||
|
||||
void Lan::Send(const NetState& s) {
|
||||
if (!active) return;
|
||||
|
||||
// One more copy of every pending hit: clients send to the host, the host to the target only
|
||||
for (Impl::PendingHit& ph : impl->pendingHits) {
|
||||
if (mode == Mode::Client) impl->SendHitPacket(impl->hostAddr, ph.pkt);
|
||||
else for (const Impl::Peer& to : impl->peers)
|
||||
if (to.id == ph.pkt.target) impl->SendHitPacket(to.addr, ph.pkt);
|
||||
ph.copiesLeft--;
|
||||
}
|
||||
impl->pendingHits.erase(
|
||||
std::remove_if(impl->pendingHits.begin(), impl->pendingHits.end(),
|
||||
[](const Impl::PendingHit& ph) { return ph.copiesLeft <= 0; }),
|
||||
impl->pendingHits.end());
|
||||
|
||||
// One more copy of every pending blast (to the host, or from the host to everyone)
|
||||
for (Impl::PendingBlast& pb : impl->pendingBlasts) {
|
||||
if (mode == Mode::Client) impl->SendBlastPacket(impl->hostAddr, pb.pkt);
|
||||
else for (const Impl::Peer& to : impl->peers) impl->SendBlastPacket(to.addr, pb.pkt);
|
||||
pb.copiesLeft--;
|
||||
}
|
||||
impl->pendingBlasts.erase(
|
||||
std::remove_if(impl->pendingBlasts.begin(), impl->pendingBlasts.end(),
|
||||
[](const Impl::PendingBlast& pb) { return pb.copiesLeft <= 0; }),
|
||||
impl->pendingBlasts.end());
|
||||
|
||||
if (mode == Mode::Client) {
|
||||
impl->SendDatagram(impl->hostAddr, { MakeEntry(0, s) });
|
||||
return;
|
||||
@@ -199,7 +309,7 @@ void Lan::Send(const NetState& s) {
|
||||
}
|
||||
}
|
||||
|
||||
void Lan::Receive(std::vector<NetUpdate>& out) {
|
||||
void Lan::Receive(std::vector<NetUpdate>& out, std::vector<NetBlast>* blasts, std::vector<NetHit>* hits) {
|
||||
if (!active) return;
|
||||
|
||||
char buf[MAX_DATAGRAM + 64];
|
||||
@@ -213,6 +323,71 @@ void Lan::Receive(std::vector<NetUpdate>& out) {
|
||||
}
|
||||
if (r < (int)sizeof(Header)) continue;
|
||||
|
||||
uint32_t magic;
|
||||
std::memcpy(&magic, buf, sizeof(magic));
|
||||
if (magic == NET_MAGIC_BLAST) {
|
||||
if (r != (int)sizeof(BlastPacket)) continue;
|
||||
BlastPacket bp;
|
||||
std::memcpy(&bp, buf, sizeof(bp));
|
||||
if (bp.version != NET_VERSION) continue;
|
||||
|
||||
if (mode == Mode::Host) {
|
||||
// Only known players may blast. Stamp their id and relay to everyone else.
|
||||
const Impl::Peer* sender = nullptr;
|
||||
for (const Impl::Peer& pe : impl->peers)
|
||||
if (SameAddress(pe.addr, from)) { sender = &pe; break; }
|
||||
if (!sender) continue;
|
||||
bp.sender = sender->id;
|
||||
for (const Impl::Peer& to : impl->peers)
|
||||
if (to.id != sender->id) impl->SendBlastPacket(to.addr, bp);
|
||||
}
|
||||
else if (!SameAddress(from, impl->hostAddr)) continue; // clients only listen to the host
|
||||
|
||||
if (blasts && impl->FirstSighting(0, bp.sender, bp.id)) {
|
||||
NetBlast nb;
|
||||
nb.sender = bp.sender;
|
||||
nb.id = bp.id;
|
||||
nb.center = glm::vec3(bp.center[0], bp.center[1], bp.center[2]);
|
||||
nb.radius = bp.radius;
|
||||
nb.power = bp.power;
|
||||
blasts->push_back(nb);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (magic == NET_MAGIC_HIT) {
|
||||
if (r != (int)sizeof(HitPacket)) continue;
|
||||
HitPacket hp;
|
||||
std::memcpy(&hp, buf, sizeof(hp));
|
||||
if (hp.version != NET_VERSION) continue;
|
||||
|
||||
bool forUs = true;
|
||||
if (mode == Mode::Host) {
|
||||
// Only known players may shoot. Stamp their id; deliver to us or forward to the target.
|
||||
const Impl::Peer* sender = nullptr;
|
||||
for (const Impl::Peer& pe : impl->peers)
|
||||
if (SameAddress(pe.addr, from)) { sender = &pe; break; }
|
||||
if (!sender) continue;
|
||||
hp.sender = sender->id;
|
||||
if (hp.target != 0) {
|
||||
forUs = false;
|
||||
for (const Impl::Peer& to : impl->peers)
|
||||
if (to.id == hp.target) impl->SendHitPacket(to.addr, hp);
|
||||
}
|
||||
}
|
||||
else if (!SameAddress(from, impl->hostAddr)) continue; // clients only listen to the host
|
||||
|
||||
if (forUs && hits && impl->FirstSighting(1, hp.sender, hp.id)) {
|
||||
NetHit nh;
|
||||
nh.sender = hp.sender;
|
||||
nh.id = hp.id;
|
||||
nh.damage = hp.damage;
|
||||
nh.knockback = glm::vec3(hp.knock[0], hp.knock[1], hp.knock[2]);
|
||||
hits->push_back(nh);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
Header h;
|
||||
std::memcpy(&h, buf, sizeof(h));
|
||||
if (h.magic != NET_MAGIC || h.version != NET_VERSION) continue;
|
||||
|
||||
Reference in New Issue
Block a user