网络层

This commit is contained in:
2025-06-28 17:38:22 +08:00
parent 54dc7ba173
commit 605197345b
20 changed files with 482 additions and 376 deletions

View File

@@ -1,175 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>游戏方块容器</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body, html {
height: 100%;
overflow: hidden;
background-color: #f0f0f0; /* 页面背景色 */
}
.grid-container {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: repeat(4, 1fr);
gap: 15px;
width: min(100vw, 100vh); /* 取视口宽高的较小值 */
height: min(100vw, 100vh); /* 确保容器是正方形 */
padding: 20px;
background-color: white; /* 容器背景为白色 */
margin: 0 auto; /* 水平居中 */
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%); /* 完全居中 */
}
.grid-item {
background-color: white; /* 大方块背景为白色 */
border: 2px solid #e0e0e0; /* 柔和的边框 */
border-radius: 6px;
display: flex;
flex-wrap: wrap; /* 允许内部小方块换行 */
position: relative; /* 为内部元素定位做准备 */
overflow: hidden; /* 隐藏超出部分 */
/* 不再需要aspect-ratio因为grid布局会自动保持正方形 */
}
/* 内部小方块样式 */
.inner-square {
width: 25%; /* 4x4网格每个占25%宽度 */
height: 25%; /* 4x4网格每个占25%高度 */
border: 1px solid #f5f5f5; /* 浅色边框分隔 */
box-sizing: border-box;
}
/* 状态指示器 */
.status-indicator {
position: absolute;
top: 5px;
right: 5px;
width: 10px;
height: 10px;
border-radius: 50%;
background-color: #ccc; /* 默认灰色(未连接) */
}
.status-connected {
background-color: #2ecc71; /* 连接成功时绿色 */
}
.status-error {
background-color: #e74c3c; /* 错误时红色 */
}
</style>
</head>
<body>
<div class="grid-container">
<script>
document.addEventListener('DOMContentLoaded', () => {
const container = document.querySelector('.grid-container');
// 创建16个大方块容器
for (let i = 1; i <= 16; i++) {
const gridItem = document.createElement('div');
gridItem.className = 'grid-item';
gridItem.id = `grid-${i}`; // 为每个方块设置唯一ID
// 添加状态指示器
const statusIndicator = document.createElement('div');
statusIndicator.className = 'status-indicator';
gridItem.appendChild(statusIndicator);
// 在方块内部创建4x4小方块
for (let j = 0; j < 16; j++) {
const innerSquare = document.createElement('div');
innerSquare.className = 'inner-square';
gridItem.appendChild(innerSquare);
}
container.appendChild(gridItem);
// 为每个大方块创建独立的WebSocket连接
connectWebSocket(gridItem, i);
}
// WebSocket连接函数
function connectWebSocket(gridItem, index) {
// 替换为实际的WebSocket服务器地址
const wsUrl = `ws://localhost:8080/game/${index}`;
const ws = new WebSocket(wsUrl);
// 获取状态指示器
const statusIndicator = gridItem.querySelector('.status-indicator');
ws.onopen = () => {
console.log(`方块 ${index} 已连接到服务器`);
statusIndicator.classList.add('status-connected');
statusIndicator.classList.remove('status-error');
// 发送初始化消息(示例)
ws.send(JSON.stringify({
type: 'init',
gridId: index
}));
};
ws.onmessage = (event) => {
console.log(`方块 ${index} 收到消息:`, event.data);
// 在此处理服务器消息,更新小方块状态
// 示例:根据消息改变小方块颜色
const data = JSON.parse(event.data);
if (data.type === 'update') {
updateInnerSquares(gridItem, data);
}
};
ws.onerror = (error) => {
console.error(`方块 ${index} 连接错误:`, error);
statusIndicator.classList.remove('status-connected');
statusIndicator.classList.add('status-error');
};
ws.onclose = () => {
console.log(`方块 ${index} 连接关闭`);
statusIndicator.classList.remove('status-connected');
};
// 将WebSocket实例附加到DOM元素上
gridItem.ws = ws;
}
// 更新小方块状态的函数(示例)
function updateInnerSquares(gridItem, data) {
const innerSquares = gridItem.querySelectorAll('.inner-square');
innerSquares.forEach((square, idx) => {
// 根据服务器数据更新小方块样式
// 示例:随机改变背景色
if (data.squares && data.squares[idx]) {
square.style.backgroundColor = data.squares[idx].color;
}
});
}
// 窗口关闭时关闭所有WebSocket连接
window.addEventListener('beforeunload', () => {
document.querySelectorAll('.grid-item').forEach(item => {
if (item.ws && item.ws.readyState === WebSocket.OPEN) {
item.ws.close();
}
});
});
});
</script>
</div>
</body>
</html>

103
Client/web/game.js Normal file
View File

@@ -0,0 +1,103 @@
const { createApp } = Vue;
const App = {
template: `
<div class="app-container">
<div class="grid-container">
<div class="square" v-for="(square, index) in squares" :key="index">
<div class="connection-dot" :class="{ connected: square.connected }"></div>
</div>
</div>
</div>
`,
data() {
return {
squares: [
{ connected: false, ws: null },
{ connected: false, ws: null },
{ connected: false, ws: null },
{ connected: false, ws: null }
]
}
},
mounted() {
// 为每个正方形建立独立的WebSocket连接
this.squares.forEach((square, index) => {
this.connectWebSocket(square, index);
});
},
methods: {
connectWebSocket(square, index) {
// 替换为实际的WebSocket服务器地址
const wsUrl = `ws://localhost:8501/?token=${index + 1}`;
try {
square.ws = new WebSocket(wsUrl);
square.ws.onopen = () => {
square.connected = true;
console.log(`WebSocket ${index + 1} connected`);
// 连接建立后发送初始消息
try {
square.ws.send(JSON.stringify({
type: "init"
}));
console.log(`Initial message sent to WebSocket ${index + 1}`);
} catch (error) {
console.error(`Failed to send initial message to WebSocket ${index + 1}:`, error);
}
};
square.ws.onclose = (e) => {
square.connected = false;
console.log(`WebSocket ${index + 1} disconnected`);
console.log(e.code, e.reason, e.wasClean)
// 尝试重新连接
// setTimeout(() => this.connectWebSocket(square, index), 1000);
};
square.ws.onerror = (error) => {
console.error(`WebSocket ${index + 1} error:`, error);
};
square.ws.onmessage = (event) => {
console.log(`WebSocket ${index + 1} message:`, event.data);
// 处理接收到的消息
try {
const message = JSON.parse(event.data);
const arr = JSON.parse(message.data);
if (Array.isArray(arr) && arr.length === 2) {
const [x, y] = arr;
console.log(`Creating dot at (${x}, ${y}) for square ${index}`);
const squareElement = document.querySelectorAll('.square')[index];
if (!squareElement) {
console.error('Square element not found');
return;
}
// 创建圆点元素
const dot = document.createElement('div');
dot.className = 'game-dot';
dot.style.left = `${x}px`;
dot.style.top = `${y}px`;
dot.style.zIndex = '10';
// 添加到游戏场景
squareElement.appendChild(dot);
console.log('Dot added successfully');
}
} catch (error) {
console.error('Error processing message:', error);
}
};
} catch (error) {
console.error(`WebSocket ${index + 1} init error:`, error);
}
}
}
};
createApp(App).mount('#app');

17
Client/web/index.html Normal file
View File

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>游戏方块容器</title>
<link rel="stylesheet" href="style.css">
<script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app"></div>
<script src="game.js"></script>
</body>
</html>

80
Client/web/style.css Normal file
View File

@@ -0,0 +1,80 @@
/* 重置默认边距 */
body, html {
margin: 0;
padding: 0;
height: 100%;
background-color: #f5f5f5;
}
/* 应用容器填满整个视口并居中 */
.app-container {
width: 100%;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
/* 重置body样式 */
body {
margin: 0;
padding: 0;
overflow: hidden;
}
/* 网格容器 - 固定2x2布局 */
.grid-container {
display: grid;
grid-template-columns: 400px 400px;
grid-template-rows: 400px 400px;
gap: 30px;
padding: 40px;
background-color: #e0e0e0;
min-height: 100vh;
width: 100vw;
box-sizing: border-box;
margin: 0 auto;
place-content: center;
}
/* 正方形样式 */
.square {
width: 400px;
height: 400px;
background-color: #ffffff;
background-image:
linear-gradient(to right, #ddd 1px, transparent 1px),
linear-gradient(to bottom, #ddd 1px, transparent 1px);
background-size: 20px 20px;
border: 5px solid #555;
border-radius: 15px;
position: relative;
box-shadow: 0 6px 12px rgba(0,0,0,0.15);
}
/* 连接状态圆点 */
.connection-dot {
position: absolute;
top: 15px;
right: 15px;
width: 20px;
height: 20px;
border-radius: 50%;
background-color: #ff4444; /* 默认断开状态-红色 */
transition: background-color 0.3s;
}
.connection-dot.connected {
background-color: #44ff44; /* 连接状态-绿色 */
}
/* 游戏动态圆点 */
.game-dot {
position: absolute;
width: 10px;
height: 10px;
background-color: red;
border-radius: 50%;
transform: translate(-50%, -50%);
pointer-events: none;
}