世界、玩家登录、加入广播。

This commit is contained in:
janing
2025-12-14 22:36:05 +08:00
commit 4dc5fc6cca
34 changed files with 2965 additions and 0 deletions

View File

@@ -0,0 +1,74 @@
import { ApiCall } from "tsrpc";
import { ReqLogin, ResLogin, PlayerInfo } from "../shared/protocols/PtlLogin";
import { playerManager } from "../managers/PlayerManager";
/**
* 登录API
* 处理玩家登录流程:
* 1. 查找已有角色
* 2. 若无角色,自动注册并创建角色
* 3. 分配出生点
* 4. 返回角色信息
*/
export default async function (call: ApiCall<ReqLogin, ResLogin>) {
const { playerId, playerName } = call.req;
// 验证玩家ID
if (!playerId || playerId.trim().length === 0) {
call.error('玩家ID不能为空');
return;
}
try {
// 检查玩家是否已存在
const isNewPlayer = !playerManager.hasPlayer(playerId);
// 获取或创建玩家(自动处理注册和创角)
const player = playerManager.getOrCreatePlayer(playerId, playerName);
// 设置玩家在线状态
playerManager.setPlayerOnline(playerId, call.conn.id);
// 保存玩家ID到连接对象供其他API使用
(call.conn as any).playerId = playerId;
// 转换为协议格式
const playerInfo: PlayerInfo = {
id: player.id,
name: player.name,
position: { ...player.position },
spawnPoint: { ...player.spawnPoint },
hp: player.hp,
maxHp: player.maxHp,
isAlive: player.isAlive,
createdAt: player.createdAt,
lastLoginAt: player.lastLoginAt
};
// 广播玩家加入消息给其他在线玩家
const { broadcastToAll } = await import('../utils/broadcast');
broadcastToAll('PlayerJoin', {
playerId: player.id,
playerName: player.name,
position: { ...player.position },
isNewPlayer,
timestamp: Date.now()
}, call.conn.id);
// 返回成功结果
call.succ({
success: true,
message: isNewPlayer ? '创建角色成功,欢迎来到游戏世界!' : '登录成功,欢迎回来!',
player: playerInfo,
isNewPlayer
});
console.log(`玩家 ${player.name} (${playerId}) ${isNewPlayer ? '首次登录' : '登录成功'}`);
console.log(` 在线玩家数: ${playerManager.getOnlinePlayerCount()}`);
} catch (error) {
console.error('登录失败:', error);
call.error(`登录失败: ${error instanceof Error ? error.message : '未知错误'}`);
}
}

86
server/src/api/ApiMove.ts Normal file
View File

@@ -0,0 +1,86 @@
import { ApiCall } from "tsrpc";
import { ReqMove, ResMove } from "../shared/protocols/PtlMove";
import { playerManager } from "../managers/PlayerManager";
import { broadcastToAll } from "../utils/broadcast";
import { MsgPlayerMove } from "../shared/protocols/MsgPlayerMove";
/**
* 移动API
* 处理玩家移动请求,验证位置合法性,更新玩家位置并广播给其他玩家
*/
export default async function (call: ApiCall<ReqMove, ResMove>) {
const { x, y } = call.req;
// 从连接中获取玩家ID需要在登录时保存
const playerId = (call.conn as any).playerId;
if (!playerId) {
call.error('未登录,请先登录');
return;
}
// 获取玩家信息
const player = playerManager.getPlayer(playerId);
if (!player) {
call.error('玩家不存在');
return;
}
// 检查玩家是否存活
if (!player.isAlive) {
call.error('玩家已死亡,无法移动');
return;
}
// 验证坐标是否为数字
if (typeof x !== 'number' || typeof y !== 'number') {
call.error('坐标格式错误');
return;
}
// 验证坐标是否为整数
if (!Number.isInteger(x) || !Number.isInteger(y)) {
call.error('坐标必须为整数');
return;
}
try {
// 尝试更新玩家位置
const success = playerManager.updatePlayerPosition(playerId, x, y);
if (!success) {
call.succ({
success: false,
message: '移动失败,位置无效或超出世界边界'
});
return;
}
// 获取更新后的位置
const newPosition = { x, y };
// 广播移动消息给所有其他玩家
const moveMsg: MsgPlayerMove = {
playerId: player.id,
playerName: player.name,
position: newPosition,
timestamp: Date.now()
};
broadcastToAll('MsgPlayerMove', moveMsg, call.conn.id);
// 返回成功结果
call.succ({
success: true,
message: '移动成功',
position: newPosition
});
console.log(`玩家 ${player.name} 移动到 (${x}, ${y})`);
} catch (error) {
console.error('移动失败:', error);
call.error(`移动失败: ${error instanceof Error ? error.message : '未知错误'}`);
}
}

26
server/src/api/ApiSend.ts Normal file
View File

@@ -0,0 +1,26 @@
import { ApiCall } from "tsrpc";
import { server } from "..";
import { ReqSend, ResSend } from "../shared/protocols/PtlSend";
// This is a demo code file
// Feel free to delete it
export default async function (call: ApiCall<ReqSend, ResSend>) {
// Error
if (call.req.content.length === 0) {
call.error('Content is empty')
return;
}
// Success
let time = new Date();
call.succ({
time: time
});
// Broadcast
server.broadcastMsg('Chat', {
content: call.req.content,
time: time
})
}