楚天

惟楚有材,于斯为盛

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment

栈的注意事项

time:2026_1_22

引用修改

char * 指针能使用任意类型,可以对任意类型的进行字节修改

内存分配

元素在栈上的空间可能不连续,这是由于编译器在编译过程中会产生的优化,可能会出现原本连续的元素,但是其中的元素没有使用,编译优化就不进行字节分配,然后通过其它指针进行访问就会出发未定义行为。
为了防止为定义行为最好还是用start进行封装

malloc 分配的时候默认会安装最大字节进行分配 16字节

new 智能分配字节

Client 实现分析

time:2026_1_21

app 依赖包

ClientRender
可视化

GameConfig
client 基础信息参数 后续可以扩展到server 公用一个确保准确

InputPredicition
预测 x,y 的动作

主体部分

ClientCtx

lab::net::UdpSocket sock; 发收
lab::net::UdpAddr server{}; server 接受

localHist 本地
remoteHist 预测
stateHist 权威

BuildCmdVec (localPid localCmd remoteCmds)
通过pid识别是否是本段,保存一个vectorcmds size = 2的状态,本段保存输入,对端保存预测
return (输入, 预测)// vectorcmds size 2

GetRemoteCmdForTick ctx pid t

return (预测)

ApplyAuthoritativeState ctx st

整理

主要就就两条思路 接受处理,发收处理维护不同的状态

OnUdp
Recv:只负责收包 -> 更新 ack/state -> 更新对手预测/触发回滚

网络开发总结

模块

NetCodec

  1. Input
    ID,count, client(本端)最新tick, clientAckServerTick(server确认最新tick) cmd包

  2. Ack
    权威tick, 最后tick 以及hash验证

  3. Statae
    权威状态包

  4. Start
    游戏开始通知

NetStub

废弃 tick 模拟状态发送和打印监控,状态发送已改为键盘输入

Packets

  1. PacketType 状态,input ack state start
  2. InputPacket 包 冗余发松多个InputCmd 包以防丢包
  3. start 包
    对局状态信息

UdpSocket

udp 连接比tcp简单,持续监听端口

游戏设计者模式

time:2026_1_21

单例模式

通常用于游戏中的全局管理类,保证整个程序(进程)中只有一个实例对象存在。只有第一次调用会进行初始化,后面调用保持原始状态。

1
2
3
4
5
6
7
8
9
10
11
class Game{
public:
static Game *getgame(){
if(game == nullptr){
game = new(Game);
}
return game;
}
private:
static Game *game
}

模板模式

把共同的部分集中到一个基类,把不同的细节部分留给子类实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
struct Character{
protected:
virtual void drow();
virtual void move();
public:
void updata(){
move();
move();
move();
drow();
}
};
struct Game{
vector<character *> chars;
void updata(){
for(auto && c : chars){
c->updata();
}
}
}

状态模式

为了解决枚举问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
struct Monster;

struct State {
virtual void update(Monster *monster) = 0;
};

struct Idle : State {
void update(Monster *monster) override {
if (monster->seesPlayer()) {
monster->setState(new Chase());
}
}
};

struct Chase : State {
void update(Monster *monster) override {
if (monster->canAttack()) {
monster->setState(new Attack());
} else if (!monster->seesPlayer()) {
monster->setState(new Idle());
}
}
};

struct Attack : State {
void update(Monster *monster) override {
if (!monster->seesPlayer()) {
monster->setState(new Idle());
}
}
};

struct Monster {
State *state = new Idle();

void update() {
state->update(this);
}

void setState(State *newState) {
delete state;
state = newState;
}
};

原型模式

主要解决问题时深拷贝的问题,因为在虚函数会导致的拷贝构造函数会出现拷贝不完全的情况,所以要采用原型模型进行完整数据类型拷贝

1
2
3
// 视线深拷贝
Ball *ball = new RedBall();
Ball *newball = new BedBall(*dynamic_cast<RedBall *>(ball));

原型模式将对象的拷贝方法作为虚函数,返回一个虚接口的指针,避免了直接拷贝类型。但虚函数内部会调用子类真正的构造函数,实现深拷贝。

1
2
3
4
5
6
7
8
9
10
11
12
13
struct Ball{
virtual thread_ptr(Ball) *close() = 0;//加入thread_ptr 实现内存智能管理
};

struct RedBall :Ball{
thread_ptr(Ball) *close() override{
return new RedBall(*this);
}
int x ;// 如果有成员变量,也会一并被拷贝到
}

Ball *ball = new RedBall();
Ball *ball_2 = ball->close(); //视线深拷贝,

CRTP 模式自动实现 clone CRTP能将子类加入模版中已放置重复定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
struct Ball {
virtual unique_ptr<Ball> clone() = 0;
};

template <class Derived>
struct BallImpl : Ball { // 自动实现 clone 的辅助工具类
unique_ptr<Ball> clone() override {
Derived *that = static_cast<Derived *>(this);
return make_unique<Derived>(*that);
}
};

struct RedBall : BallImpl<RedBall> {
// unique_ptr<Ball> clone() override { // BallImpl 自动实现的 clone 等价于
// return make_unique<RedBall>(*this); // 调用 RedBall 的拷贝构造函数
// }
};

struct BlueBall : BallImpl<BlueBall> {
// unique_ptr<Ball> clone() override { // BallImpl 自动实现的 clone 等价于
// return make_unique<BlueBall>(*this); // 调用 BlueBall 的拷贝构造函数
// }
};


Ball *ball = new RedBall();
Ball *ball_2 = ball->close(); //视线深拷贝,

组件模式

1
2
3
4
struct connect{
virtual void updata()
}

观察者模式

发布-订阅模式

访问者模式

服务器分析

time:2026_1_20

ClientConn
历史缓存
tick
最后输入tick
最后权威tick
最后输入包
玩家数量

ServerCtx
基础信息
对局设置
帧同步设置
世界设置
sock 网络
clients 存储客户端状态
函数
AssignSlot 分配id 1,2
GetPlayer 从ServerCtx->clients 取出ClientConn
OnlineCount 在线玩家统计
GetCmdForTick 从ClientConn中获取动作信息,正常接受,如果出现延迟但小于延迟设置,着延续上一次包,但是出现过大延迟直接采用默认信息

OnUdp 收包加入缓存
MaybeStartMatch for pid 轮训发收start包,其中通过利用getplayer 吧serverctx中的clientconn取出,同时检测ctx中的状态已start则跳过

OnTick 
    整体事件监听
    推进世界 + 给两边发 State/Ack

工厂设计模式

time:2026_1_20

虚函数

virtual
即使通过基类的指针或引用调用该函数,实际调用的函数是派生类中重写的版本
override
如果派生类在虚函数声明时使用了override描述符,那么该函数必须重载其基类中的同名函数,否则代码将无法通过编译
virtual 定义了父类中的子类需要重载的函数,override 则是确保子类在继承父类的时候必须进行重载,以防后续错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
struct pet{
virtual void speak() = 0;
};
struct cat::pet{
void speak() override{
print("miao");
}
};
struct dog::pet{
void speak() override{
print("wano");
}
};
void feed(Pet *pet) {
puts("喂食");
pet->speak();
puts("喂食完毕");
}
int main(){
pet *cat = new cat();
pet *dog = new dog();
freed(cat);
freed(dog);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
sturct Reduce(){
virtual int init() = 0;
virtual int add(int a,int b) = 0;
};

struct add_our :: Reduce(){
int init() override{
return 0;
}
int add(int a ,int b) override{
return a+b;
}
};
struct chen_our :: Reduce(){
int init() override{
return 1;
}
int add(int a,int b) override{
return a*b;
}
};

int reduce(std::vector<int> v,Reduce & reducer){
int x =reducer->int();
for(int i = 0;i<v.size();i++ ){
x = reducer->add(x, v[i]);
}
return x
}

工厂模式

享元模式

享元模式:共享多个对象之间相同的部分,节省内存开销
共享同智能指标share_ptr实现多个对象指向同个资源在减少资源开销的同时,也通过rall特性完成智能内存管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct Sprite{
vector<char> texture;
void draw(glm::vec3 posoition){
glDrawPixels(posoition, texture);
}
};
struct Bullet{
glm::vec3 posoition;
glm::vec3 velocity;
shared_ptr<Sprite> sprite;

void draw(){
sprite->draw(position, velocity);
}
}

代理模式

生产消费者模式

time:26_1_19
多线程开发,生成消费者模式,关键就在与三个参数。互斥。通知 和队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#include <iostream>
#include <thread>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <functional>

// 线程安全队列模板类
template <typename T>
class SafeQueue {
public:
SafeQueue() = default;

// 禁用拷贝构造和赋值操作
SafeQueue(const SafeQueue&) = delete;
SafeQueue& operator=(const SafeQueue&) = delete;

// 入队操作
void enqueue(T item) {
std::lock_guard<std::mutex> lock(m_mutex);
m_queue.push(std::move(item));
m_cond.notify_one();
}

// 出队操作,阻塞等待直到队列非空
T dequeue() {
std::unique_lock<std::mutex> lock(m_mutex);
m_cond.wait(lock, [this]{ return !m_queue.empty(); });
T item = std::move(m_queue.front());
m_queue.pop();
return item;
}

// 尝试出队,不阻塞
bool try_dequeue(T& item) {
std::lock_guard<std::mutex> lock(m_mutex);
if(m_queue.empty()) return false;
item = std::move(m_queue.front());
m_queue.pop();
return true;
}

bool empty() const {
std::lock_guard<std::mutex> lock(m_mutex);
return m_queue.empty();
}

private:
mutable std::mutex m_mutex; // 互斥
std::queue<T> m_queue; // 队列
std::condition_variable m_cond;// 通知
};

// 测试队列和线程
int main() {
SafeQueue<std::function<void()>> tasks;

// 生产者线程
std::thread producer([&tasks]() {
for(int i = 1; i <= 5; i++) {
tasks.enqueue([i]() { std::cout << "Task " << i << " executed"; });
}
});

// 消费者线程
std::thread consumer([&tasks]() {
for(int i = 1; i <= 5; i++) {
auto task = tasks.dequeue();
task();
}
});

producer.join();
consumer.join();

return 0;
}