大更新,太多了,具体进游戏查看详细更新内容

反正很多
This commit is contained in:
2025-05-27 11:09:09 +08:00
parent a1e71a6a79
commit 8215cfa3ee
382 changed files with 13838 additions and 2974 deletions

100
Server/JsonEdit/README.md Normal file
View File

@@ -0,0 +1,100 @@
# JSON 批量编辑器
一个简洁强大的JSON编辑工具专门用于批量添加键值对到JSON文件中的所有对象。
## 🚀 功能特点
- **批量添加属性**: 一键为JSON中所有对象添加新的键值对
- **智能类型识别**: 自动识别并转换数据类型
- **支持所有JSON数据类型**: 字符串、数字、布尔值、对象、数组、null
- **文件上传**: 支持JSON文件拖拽上传
- **即时下载**: 编辑完成后立即下载修改后的JSON文件
- **无需服务器**: 纯前端实现,直接在浏览器中运行
## 📋 支持的数据类型
| 类型 | 输入示例 | 转换结果 |
|------|----------|----------|
| 字符串 | `hello world` | `"hello world"` |
| 整数 | `123` | `123` |
| 小数 | `3.14` | `3.14` |
| 布尔值 | `true``false` | `true``false` |
| 空值 | `null` | `null` |
| 对象 | `{"key": "value"}` | `{"key": "value"}` |
| 数组 | `[1, 2, 3]` | `[1, 2, 3]` |
| 空字符串 | *(留空)* | `""` |
## 🎯 使用方法
### 1. 加载JSON数据
- **方法一**: 点击"上传JSON文件"按钮选择文件
- **方法二**: 直接在编辑器中粘贴JSON数据
- **方法三**: 点击"加载示例数据"使用预设数据
### 2. 批量添加属性
1. 在"键名"字段输入要添加的属性名,如: `能否购买`
2. 在"键值"字段输入属性值,如: `true`
3. 点击"批量添加到所有对象"按钮
4. 系统会自动为JSON中的每个对象添加该属性
### 3. 下载结果
点击"下载修改后的JSON"按钮保存编辑后的文件
## 💡 使用示例
### 原始JSON:
```json
{
"小麦": {
"花费": 120,
"收益": 100,
"品质": "普通"
},
"稻谷": {
"花费": 100,
"收益": 120,
"品质": "普通"
}
}
```
### 添加属性: 键名=`能否购买`, 键值=`true`
### 结果JSON:
```json
{
"小麦": {
"花费": 120,
"收益": 100,
"品质": "普通",
"能否购买": true
},
"稻谷": {
"花费": 100,
"收益": 120,
"品质": "普通",
"能否购买": true
}
}
```
## 🔧 快速开始
1. 直接在浏览器中打开 `templates/json_editor.html` 文件
2. 无需安装任何依赖或服务器
3. 开始使用批量编辑功能
## ⚠️ 注意事项
- 工具会递归处理嵌套对象,为所有找到的对象添加指定属性
- 数组元素如果是对象,也会被添加属性
- 确保JSON格式正确否则无法处理
- 修改前建议备份原始文件
## 🎨 界面说明
- **左侧边栏**: 文件操作、批量编辑功能、快速示例
- **右侧编辑区**: JSON数据显示和编辑
- **智能提示**: 实时显示操作结果和错误信息
这个工具特别适合游戏开发、配置文件管理等需要批量修改JSON数据的场景。

View File

@@ -0,0 +1,65 @@
# JSON格式化示例
以下展示三种不同的JSON格式化效果
## 原始数据
```json
{"小麦":{"花费":120,"收益":100,"品质":"普通"},"稻谷":{"花费":100,"收益":120,"品质":"普通"}}
```
## 1. 标准格式化2空格缩进
```json
{
"小麦": {
"花费": 120,
"收益": 100,
"品质": "普通"
},
"稻谷": {
"花费": 100,
"收益": 120,
"品质": "普通"
}
}
```
## 2. 最小化(压缩)
```json
{"小麦":{"花费":120,"收益":100,"品质":"普通"},"稻谷":{"花费":100,"收益":120,"品质":"普通"}}
```
## 3. 一行化(一个对象一行)
```json
{
"小麦": {"花费":120,"收益":100,"品质":"普通"},
"稻谷": {"花费":100,"收益":120,"品质":"普通"}
}
```
## 使用场景
- **标准格式化**: 适合阅读和编辑,开发时使用
- **最小化**: 适合网络传输,节省带宽
- **一行化**: 适合比较不同对象,每行一个对象便于查看差异
## 批量添加属性示例
添加键名: `能否购买`, 键值: `true`
### 结果:
```json
{
"小麦": {
"花费": 120,
"收益": 100,
"品质": "普通",
"能否购买": true
},
"稻谷": {
"花费": 100,
"收益": 120,
"品质": "普通",
"能否购买": true
}
}
```

View File

@@ -0,0 +1,267 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from flask import Flask, request, jsonify, render_template, send_file
import json
import os
from datetime import datetime
app = Flask(__name__)
class JSONFormatter:
"""JSON格式化工具类"""
@staticmethod
def format_standard(data, indent=2):
"""标准格式化 - 带缩进的可读格式"""
return json.dumps(data, ensure_ascii=False, indent=indent)
@staticmethod
def minify(data):
"""最小化 - 压缩去除空格"""
return json.dumps(data, ensure_ascii=False, separators=(',', ':'))
@staticmethod
def one_line_per_object(data):
"""一行化 - 每个对象/元素占一行"""
if isinstance(data, list):
# 如果是数组,每个元素占一行
lines = ['[']
for i, item in enumerate(data):
comma = ',' if i < len(data) - 1 else ''
lines.append(f' {json.dumps(item, ensure_ascii=False)}{comma}')
lines.append(']')
return '\n'.join(lines)
elif isinstance(data, dict):
# 如果是对象,每个键值对占一行
lines = ['{']
keys = list(data.keys())
for i, key in enumerate(keys):
comma = ',' if i < len(keys) - 1 else ''
value_str = json.dumps(data[key], ensure_ascii=False)
lines.append(f' {json.dumps(key, ensure_ascii=False)}: {value_str}{comma}')
lines.append('}')
return '\n'.join(lines)
else:
# 基本类型直接返回
return json.dumps(data, ensure_ascii=False)
@app.route('/')
def index():
"""主页"""
return render_template('json_editor.html')
@app.route('/api/format', methods=['POST'])
def format_json():
"""JSON格式化API"""
try:
data = request.get_json()
content = data.get('content', '')
format_type = data.get('format_type', 'standard') # standard, minify, oneline
if not content.strip():
return jsonify({'success': False, 'message': '请提供JSON内容'})
# 解析JSON
try:
json_data = json.loads(content)
except json.JSONDecodeError as e:
return jsonify({'success': False, 'message': f'JSON格式错误: {str(e)}'})
# 根据类型格式化
formatter = JSONFormatter()
if format_type == 'standard':
formatted = formatter.format_standard(json_data)
message = 'JSON标准格式化完成'
elif format_type == 'minify':
formatted = formatter.minify(json_data)
message = 'JSON最小化完成'
elif format_type == 'oneline':
formatted = formatter.one_line_per_object(json_data)
message = 'JSON一行化格式完成'
else:
return jsonify({'success': False, 'message': '不支持的格式化类型'})
return jsonify({
'success': True,
'message': message,
'formatted': formatted,
'original_length': len(content),
'formatted_length': len(formatted)
})
except Exception as e:
return jsonify({'success': False, 'message': f'处理错误: {str(e)}'})
@app.route('/api/batch_add', methods=['POST'])
def batch_add_property():
"""批量添加属性API"""
try:
data = request.get_json()
content = data.get('content', '')
key_name = data.get('key_name', '')
key_value = data.get('key_value', '')
if not content.strip():
return jsonify({'success': False, 'message': '请提供JSON内容'})
if not key_name.strip():
return jsonify({'success': False, 'message': '请提供键名'})
# 解析JSON
try:
json_data = json.loads(content)
except json.JSONDecodeError as e:
return jsonify({'success': False, 'message': f'JSON格式错误: {str(e)}'})
# 智能解析键值
parsed_value = parse_value(key_value)
# 批量添加属性
count = add_property_to_all_objects(json_data, key_name, parsed_value)
# 格式化输出
formatted = JSONFormatter.format_standard(json_data)
return jsonify({
'success': True,
'message': f'成功为 {count} 个对象添加了属性 "{key_name}": {json.dumps(parsed_value, ensure_ascii=False)}',
'formatted': formatted,
'count': count
})
except Exception as e:
return jsonify({'success': False, 'message': f'处理错误: {str(e)}'})
def parse_value(value_str):
"""智能解析值的类型"""
if value_str == '':
return ''
# null
if value_str.lower() == 'null':
return None
# boolean
if value_str.lower() == 'true':
return True
if value_str.lower() == 'false':
return False
# number
try:
if '.' in value_str:
return float(value_str)
else:
return int(value_str)
except ValueError:
pass
# JSON object or array
if (value_str.startswith('{') and value_str.endswith('}')) or \
(value_str.startswith('[') and value_str.endswith(']')):
try:
return json.loads(value_str)
except json.JSONDecodeError:
pass
# string
return value_str
def add_property_to_all_objects(obj, key, value):
"""递归为所有对象添加属性"""
count = 0
def traverse(current):
nonlocal count
if isinstance(current, dict):
current[key] = value
count += 1
# 继续递归处理嵌套对象
for val in current.values():
if isinstance(val, (dict, list)) and val != current:
traverse(val)
elif isinstance(current, list):
for item in current:
traverse(item)
traverse(obj)
return count
@app.route('/api/validate', methods=['POST'])
def validate_json():
"""JSON验证API"""
try:
data = request.get_json()
content = data.get('content', '')
if not content.strip():
return jsonify({'success': False, 'message': '请提供JSON内容'})
try:
json_data = json.loads(content)
return jsonify({
'success': True,
'message': 'JSON格式正确 ✓',
'valid': True
})
except json.JSONDecodeError as e:
return jsonify({
'success': False,
'message': f'JSON格式错误: {str(e)}',
'valid': False,
'error': str(e)
})
except Exception as e:
return jsonify({'success': False, 'message': f'验证错误: {str(e)}'})
@app.route('/api/download', methods=['POST'])
def download_json():
"""下载JSON文件"""
try:
data = request.get_json()
content = data.get('content', '')
format_type = data.get('format_type', 'standard')
if not content.strip():
return jsonify({'success': False, 'message': '没有可下载的内容'})
# 验证JSON格式
try:
json_data = json.loads(content)
except json.JSONDecodeError as e:
return jsonify({'success': False, 'message': f'JSON格式错误: {str(e)}'})
# 格式化
formatter = JSONFormatter()
if format_type == 'minify':
formatted_content = formatter.minify(json_data)
elif format_type == 'oneline':
formatted_content = formatter.one_line_per_object(json_data)
else:
formatted_content = formatter.format_standard(json_data)
# 生成文件名
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"edited_json_{format_type}_{timestamp}.json"
# 创建临时文件
temp_file = f"temp_{filename}"
with open(temp_file, 'w', encoding='utf-8') as f:
f.write(formatted_content)
return send_file(temp_file, as_attachment=True, download_name=filename)
except Exception as e:
return jsonify({'success': False, 'message': f'下载错误: {str(e)}'})
if __name__ == '__main__':
# 确保templates目录存在
os.makedirs('templates', exist_ok=True)
# 运行应用
app.run(debug=True, host='0.0.0.0', port=5000)

View File

@@ -0,0 +1,2 @@
Flask==2.3.3
Werkzeug==2.3.7

View File

@@ -0,0 +1,627 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>简易JSON编辑器 - 批量添加键值</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: #f5f5f5;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
overflow: hidden;
}
.header {
background: #4a90e2;
color: white;
padding: 20px;
text-align: center;
}
.header h1 {
margin-bottom: 5px;
}
.main-content {
display: grid;
grid-template-columns: 300px 1fr;
gap: 0;
min-height: 600px;
}
.sidebar {
background: #f8f9fa;
border-right: 1px solid #dee2e6;
padding: 20px;
}
.editor-area {
padding: 20px;
}
.section {
background: white;
border: 1px solid #dee2e6;
border-radius: 5px;
padding: 15px;
margin-bottom: 15px;
}
.section h3 {
margin-bottom: 15px;
color: #333;
}
.btn {
background: #4a90e2;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
font-size: 14px;
margin: 5px 5px 5px 0;
width: 100%;
}
.btn:hover {
background: #357abd;
}
.btn-success {
background: #28a745;
}
.btn-success:hover {
background: #218838;
}
.input-group {
margin-bottom: 15px;
}
.input-group label {
display: block;
margin-bottom: 5px;
font-weight: 500;
color: #333;
}
.input-group input {
width: 100%;
padding: 8px 12px;
border: 1px solid #dee2e6;
border-radius: 5px;
font-size: 14px;
}
.input-group input:focus {
outline: none;
border-color: #4a90e2;
}
#jsonEditor {
width: 100%;
height: 500px;
border: 1px solid #dee2e6;
border-radius: 5px;
padding: 15px;
font-family: 'Courier New', monospace;
font-size: 14px;
line-height: 1.5;
resize: vertical;
}
#jsonEditor:focus {
outline: none;
border-color: #4a90e2;
}
.file-upload {
position: relative;
display: inline-block;
cursor: pointer;
overflow: hidden;
width: 100%;
}
.file-upload input[type=file] {
position: absolute;
left: -9999px;
}
.alert {
padding: 10px 15px;
margin-bottom: 15px;
border-radius: 5px;
font-weight: 500;
}
.alert-success {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.alert-error {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.alert-info {
background: #d1ecf1;
color: #0c5460;
border: 1px solid #bee5eb;
}
@media (max-width: 768px) {
.main-content {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🔧 JSON批量编辑器</h1>
<p>批量添加键值对到JSON文件</p>
</div>
<div class="main-content">
<!-- 侧边栏 -->
<div class="sidebar">
<!-- 文件操作 -->
<div class="section">
<h3>📁 文件操作</h3>
<div class="file-upload btn">
<input type="file" id="fileInput" accept=".json" />
上传JSON文件
</div>
<button class="btn btn-success" onclick="downloadJSON()">
下载修改后的JSON
</button>
</div>
<!-- 批量添加键值 -->
<div class="section">
<h3>⚡ 批量添加键值</h3>
<div class="input-group">
<label for="keyName">键名:</label>
<input type="text" id="keyName" placeholder="例: 能否购买" />
</div>
<div class="input-group">
<label for="keyValue">键值:</label>
<input type="text" id="keyValue" placeholder="支持多种类型,见下方说明" />
</div>
<button class="btn btn-success" onclick="batchAddProperty()">
批量添加到所有对象
</button>
<div style="margin-top: 15px; padding: 10px; background: #f8f9fa; border-radius: 5px; font-size: 12px;">
<strong>支持的数据类型:</strong><br>
• 字符串: hello world<br>
• 数字: 123 或 3.14<br>
• 布尔值: true 或 false<br>
• 空值: null<br>
• 对象: {"key": "value"}<br>
• 数组: [1, 2, 3]<br>
<small style="color: #666;">系统会自动识别并转换数据类型</small>
</div>
</div>
<!-- 快速示例 -->
<div class="section">
<h3>📝 快速示例</h3>
<button class="btn" onclick="loadSampleJSON()">
加载示例数据
</button>
<div style="margin-top: 15px;">
<strong style="font-size: 12px;">快速填入示例键值:</strong><br>
<button class="btn" style="font-size: 11px; padding: 5px 10px; margin: 2px;" onclick="fillExample('能否购买', 'true')">
布尔值示例
</button>
<button class="btn" style="font-size: 11px; padding: 5px 10px; margin: 2px;" onclick="fillExample('价格', '150')">
数字示例
</button>
<button class="btn" style="font-size: 11px; padding: 5px 10px; margin: 2px;" onclick="fillExample('备注', '新增属性')">
字符串示例
</button>
<button class="btn" style="font-size: 11px; padding: 5px 10px; margin: 2px;" onclick="fillExample('tags', '[&quot;新&quot;, &quot;热门&quot;]')">
数组示例
</button>
</div>
</div>
<!-- JSON格式化操作 -->
<div class="section">
<h3>🔧 格式化操作</h3>
<button class="btn" onclick="formatJSONStandard()">
标准格式化
</button>
<button class="btn btn-success" onclick="minifyJSON()">
最小化(压缩)
</button>
<button class="btn" style="background: #17a2b8;" onclick="oneLinePerObject()">
一行化(一个对象一行)
</button>
<button class="btn" style="background: #6f42c1; color: white;" onclick="validateJSON()">
验证JSON格式
</button>
<div style="margin-top: 15px; padding: 10px; background: #f8f9fa; border-radius: 5px; font-size: 12px;">
<strong>格式化说明:</strong><br>
<strong>标准格式化</strong>: 2空格缩进易于阅读<br>
<strong>最小化</strong>: 去除空格,节省空间<br>
<strong>一行化</strong>: 每个对象占一行,便于比较<br>
<strong>验证格式</strong>: 检查JSON语法是否正确<br>
</div>
</div>
</div>
<!-- 编辑区域 -->
<div class="editor-area">
<!-- 消息区域 -->
<div id="messageArea"></div>
<!-- JSON编辑器 -->
<textarea id="jsonEditor" placeholder="在此输入或上传JSON数据..."></textarea>
</div>
</div>
</div>
<script>
// 初始化
document.addEventListener('DOMContentLoaded', function() {
document.getElementById('fileInput').addEventListener('change', handleFileUpload);
});
// 显示消息
function showMessage(message, type = 'success') {
const messageArea = document.getElementById('messageArea');
let alertClass = 'alert-success';
if (type === 'error') {
alertClass = 'alert-error';
} else if (type === 'info') {
alertClass = 'alert-info';
}
messageArea.innerHTML = `
<div class="alert ${alertClass}">
${message}
</div>
`;
setTimeout(() => {
messageArea.innerHTML = '';
}, 3000);
}
// 加载示例JSON
function loadSampleJSON() {
const sampleJSON = {
"测试作物": {
"花费": 1,
"生长时间": 3,
"收益": 9999,
"品质": "普通"
},
"小麦": {
"花费": 120,
"生长时间": 120,
"收益": 100,
"品质": "普通"
},
"稻谷": {
"花费": 100,
"生长时间": 240,
"收益": 120,
"品质": "普通"
}
};
document.getElementById('jsonEditor').value = JSON.stringify(sampleJSON, null, 2);
showMessage('示例数据已加载');
}
// 快速填入示例键值对
function fillExample(keyName, keyValue) {
document.getElementById('keyName').value = keyName;
document.getElementById('keyValue').value = keyValue;
showMessage(`已填入示例: ${keyName} = ${keyValue}`, 'info');
}
// 文件上传处理
function handleFileUpload(event) {
const file = event.target.files[0];
if (!file) return;
if (!file.name.toLowerCase().endsWith('.json')) {
showMessage('请选择JSON文件', 'error');
return;
}
const reader = new FileReader();
reader.onload = function(e) {
try {
const content = e.target.result;
JSON.parse(content); // 验证JSON格式
document.getElementById('jsonEditor').value = content;
showMessage('文件上传成功');
} catch (error) {
showMessage('JSON格式错误: ' + error.message, 'error');
}
};
reader.readAsText(file);
// 清空文件输入
event.target.value = '';
}
// 批量添加属性
function batchAddProperty() {
const keyName = document.getElementById('keyName').value.trim();
const keyValue = document.getElementById('keyValue').value.trim();
const jsonContent = document.getElementById('jsonEditor').value.trim();
if (!keyName) {
showMessage('请输入键名', 'error');
return;
}
if (!jsonContent) {
showMessage('请输入或上传JSON数据', 'error');
return;
}
try {
let jsonData = JSON.parse(jsonContent);
// 智能类型转换
let processedValue = parseValue(keyValue);
// 批量添加属性
const result = addPropertyToAllObjects(jsonData, keyName, processedValue);
if (result.count > 0) {
document.getElementById('jsonEditor').value = JSON.stringify(jsonData, null, 2);
showMessage(`成功为 ${result.count} 个对象添加了属性 "${keyName}": ${JSON.stringify(processedValue)}`);
} else {
showMessage('未找到可添加属性的对象', 'info');
}
} catch (error) {
showMessage('JSON格式错误: ' + error.message, 'error');
}
}
// 智能解析值的类型
function parseValue(value) {
// 如果输入为空,返回空字符串
if (value === '') {
return '';
}
// 尝试解析为null
if (value.toLowerCase() === 'null') {
return null;
}
// 尝试解析为undefined虽然JSON不支持但转为null
if (value.toLowerCase() === 'undefined') {
return null;
}
// 尝试解析为布尔值
if (value.toLowerCase() === 'true') {
return true;
}
if (value.toLowerCase() === 'false') {
return false;
}
// 尝试解析为数字
if (!isNaN(value) && !isNaN(parseFloat(value))) {
// 检查是否为整数
if (Number.isInteger(parseFloat(value))) {
return parseInt(value, 10);
} else {
return parseFloat(value);
}
}
// 尝试解析为JSON对象或数组
if ((value.startsWith('{') && value.endsWith('}')) ||
(value.startsWith('[') && value.endsWith(']'))) {
try {
return JSON.parse(value);
} catch (e) {
// 如果解析失败,当作字符串处理
return value;
}
}
// 默认当作字符串处理
return value;
}
// 递归为所有对象添加属性
function addPropertyToAllObjects(obj, key, value) {
let count = 0;
function traverse(current) {
if (typeof current === 'object' && current !== null) {
if (Array.isArray(current)) {
// 处理数组
current.forEach(item => traverse(item));
} else {
// 处理对象
current[key] = value;
count++;
// 继续递归处理嵌套对象
Object.values(current).forEach(val => {
if (typeof val === 'object' && val !== null && val !== current) {
traverse(val);
}
});
}
}
}
traverse(obj);
return { count };
}
// 下载JSON
function downloadJSON() {
const content = document.getElementById('jsonEditor').value.trim();
if (!content) {
showMessage('没有可下载的内容', 'error');
return;
}
try {
// 验证JSON格式
JSON.parse(content);
const blob = new Blob([content], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = `edited_json_${new Date().getTime()}.json`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
showMessage('JSON文件下载成功');
} catch (error) {
showMessage('JSON格式错误无法下载: ' + error.message, 'error');
}
}
// 标准格式化JSON
function formatJSONStandard() {
const content = document.getElementById('jsonEditor').value.trim();
if (!content) {
showMessage('请输入JSON数据', 'error');
return;
}
try {
const jsonData = JSON.parse(content);
const formatted = JSON.stringify(jsonData, null, 2);
document.getElementById('jsonEditor').value = formatted;
showMessage('JSON标准格式化完成');
} catch (error) {
showMessage('JSON格式错误: ' + error.message, 'error');
}
}
// 最小化JSON压缩
function minifyJSON() {
const content = document.getElementById('jsonEditor').value.trim();
if (!content) {
showMessage('请输入JSON数据', 'error');
return;
}
try {
const jsonData = JSON.parse(content);
const minified = JSON.stringify(jsonData);
document.getElementById('jsonEditor').value = minified;
showMessage('JSON最小化完成');
} catch (error) {
showMessage('JSON格式错误: ' + error.message, 'error');
}
}
// 一行化格式(一个对象一行)
function oneLinePerObject() {
const content = document.getElementById('jsonEditor').value.trim();
if (!content) {
showMessage('请输入JSON数据', 'error');
return;
}
try {
const jsonData = JSON.parse(content);
let formatted = '';
if (Array.isArray(jsonData)) {
// 如果是数组,每个元素占一行
formatted = '[\n';
jsonData.forEach((item, index) => {
formatted += ' ' + JSON.stringify(item);
if (index < jsonData.length - 1) {
formatted += ',';
}
formatted += '\n';
});
formatted += ']';
} else if (typeof jsonData === 'object' && jsonData !== null) {
// 如果是对象,每个键值对占一行
formatted = '{\n';
const keys = Object.keys(jsonData);
keys.forEach((key, index) => {
formatted += ' ' + JSON.stringify(key) + ': ' + JSON.stringify(jsonData[key]);
if (index < keys.length - 1) {
formatted += ',';
}
formatted += '\n';
});
formatted += '}';
} else {
// 基本类型直接输出
formatted = JSON.stringify(jsonData);
}
document.getElementById('jsonEditor').value = formatted;
showMessage('JSON一行化格式完成');
} catch (error) {
showMessage('JSON格式错误: ' + error.message, 'error');
}
}
// 验证JSON格式
function validateJSON() {
const content = document.getElementById('jsonEditor').value.trim();
if (!content) {
showMessage('请输入JSON数据', 'error');
return;
}
try {
JSON.parse(content);
showMessage('JSON格式验证通过');
} catch (error) {
showMessage('JSON格式错误: ' + error.message, 'error');
}
}
</script>
</body>
</html>

337
Server/QQEmailSend.py Normal file
View File

@@ -0,0 +1,337 @@
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
from email.header import Header
import random
import string
import json
import os
# 邮件发送配置
SENDER_EMAIL = '3205788256@qq.com' # 发件人邮箱
SENDER_AUTH_CODE = 'szcaxvbftusqddhi' # 授权码
SMTP_SERVER = 'smtp.qq.com' # QQ邮箱SMTP服务器
SMTP_PORT = 465 # QQ邮箱SSL端口
# 验证码缓存文件
VERIFICATION_CACHE_FILE = os.path.join("config", "verification_codes.json")
class QQMailAPI:
"""QQ邮箱发送邮件API类"""
def __init__(self, sender_email, authorization_code):
"""
初始化邮箱配置
:param sender_email: 发送方QQ邮箱地址
:param authorization_code: QQ邮箱授权码
"""
self.sender_email = sender_email
self.authorization_code = authorization_code
self.smtp_server = 'smtp.qq.com'
self.smtp_port = 465 # SSL端口
def send_text_email(self, receiver_email, subject, content, cc_emails=None):
"""
发送纯文本邮件
:param receiver_email: 接收方邮箱地址(单个)
:param subject: 邮件主题
:param content: 邮件正文内容
:param cc_emails: 抄送邮箱列表
:return: 发送成功返回True失败返回False
"""
try:
# 创建邮件对象
message = MIMEText(content, 'plain', 'utf-8')
message['From'] = Header(self.sender_email, 'utf-8')
message['To'] = Header(receiver_email, 'utf-8')
message['Subject'] = Header(subject, 'utf-8')
# 添加抄送
if cc_emails:
message['Cc'] = Header(",".join(cc_emails), 'utf-8')
all_receivers = [receiver_email] + cc_emails
else:
all_receivers = [receiver_email]
# 连接SMTP服务器并发送邮件
with smtplib.SMTP_SSL(self.smtp_server, self.smtp_port) as server:
server.login(self.sender_email, self.authorization_code)
server.sendmail(self.sender_email, all_receivers, message.as_string())
print(f"邮件发送成功:主题='{subject}', 收件人='{receiver_email}'")
return True
except Exception as e:
print(f"邮件发送失败:{str(e)}")
return False
def send_html_email(self, receiver_email, subject, html_content, cc_emails=None, attachments=None):
"""
发送HTML格式邮件可带附件
:param receiver_email: 接收方邮箱地址(单个)
:param subject: 邮件主题
:param html_content: HTML格式的邮件正文
:param cc_emails: 抄送邮箱列表
:param attachments: 附件文件路径列表
:return: 发送成功返回True失败返回False
"""
try:
# 创建带附件的邮件对象
message = MIMEMultipart()
message['From'] = Header(self.sender_email, 'utf-8')
message['To'] = Header(receiver_email, 'utf-8')
message['Subject'] = Header(subject, 'utf-8')
# 添加抄送
if cc_emails:
message['Cc'] = Header(",".join(cc_emails), 'utf-8')
all_receivers = [receiver_email] + cc_emails
else:
all_receivers = [receiver_email]
# 添加HTML正文
message.attach(MIMEText(html_content, 'html', 'utf-8'))
# 添加附件
if attachments:
for file_path in attachments:
try:
with open(file_path, 'rb') as file:
attachment = MIMEApplication(file.read(), _subtype="octet-stream")
attachment.add_header('Content-Disposition', 'attachment', filename=file_path.split("/")[-1])
message.attach(attachment)
except Exception as e:
print(f"添加附件失败 {file_path}: {str(e)}")
# 连接SMTP服务器并发送邮件
with smtplib.SMTP_SSL(self.smtp_server, self.smtp_port) as server:
server.login(self.sender_email, self.authorization_code)
server.sendmail(self.sender_email, all_receivers, message.as_string())
print(f"HTML邮件发送成功主题='{subject}', 收件人='{receiver_email}'")
return True
except Exception as e:
print(f"HTML邮件发送失败{str(e)}")
return False
class EmailVerification:
@staticmethod
def generate_verification_code(length=6):
"""
生成指定长度的随机验证码
参数:
length (int): 验证码长度默认6位
返回:
str: 生成的验证码
"""
# 生成包含大写字母和数字的验证码
chars = string.ascii_uppercase + string.digits
return ''.join(random.choice(chars) for _ in range(length))
@staticmethod
def send_verification_email(qq_number, verification_code):
"""
发送验证码邮件到QQ邮箱
参数:
qq_number (str): 接收者QQ号
verification_code (str): 验证码
返回:
bool: 发送成功返回True否则返回False
str: 成功或错误信息
"""
receiver_email = f"{qq_number}@qq.com"
# 创建邮件内容
message = MIMEText(f'''
<html>
<body>
<div style="font-family: Arial, sans-serif; color: #333;">
<h2 style="color: #4CAF50;">萌芽农场 - 邮箱验证码</h2>
<p>亲爱的玩家,您好!</p>
<p>您正在注册萌芽农场游戏账号,您的验证码是:</p>
<div style="background-color: #f2f2f2; padding: 10px; font-size: 24px; font-weight: bold; color: #4CAF50; text-align: center; margin: 20px 0;">
{verification_code}
</div>
<p>该验证码有效期为5分钟请勿泄露给他人。</p>
<p>如果这不是您本人的操作,请忽略此邮件。</p>
<p style="margin-top: 30px; font-size: 12px; color: #999;">
本邮件由系统自动发送,请勿直接回复。
</p>
</div>
</body>
</html>
''', 'html', 'utf-8')
# 修正From头格式符合QQ邮箱的要求
message['From'] = SENDER_EMAIL
message['To'] = receiver_email
message['Subject'] = Header('【萌芽农场】注册验证码', 'utf-8')
try:
# 使用SSL/TLS连接而不是STARTTLS
smtp_obj = smtplib.SMTP_SSL(SMTP_SERVER, 465)
smtp_obj.login(SENDER_EMAIL, SENDER_AUTH_CODE)
smtp_obj.sendmail(SENDER_EMAIL, [receiver_email], message.as_string())
smtp_obj.quit()
return True, "验证码发送成功"
except Exception as e:
return False, f"发送验证码失败: {str(e)}"
@staticmethod
def save_verification_code(qq_number, verification_code, expiry_time=300):
"""
保存验证码到缓存文件
参数:
qq_number (str): QQ号
verification_code (str): 验证码
expiry_time (int): 过期时间默认5分钟
返回:
bool: 保存成功返回True否则返回False
"""
import time
# 创建目录(如果不存在)
os.makedirs(os.path.dirname(VERIFICATION_CACHE_FILE), exist_ok=True)
# 读取现有的验证码数据
verification_data = {}
if os.path.exists(VERIFICATION_CACHE_FILE):
try:
with open(VERIFICATION_CACHE_FILE, 'r', encoding='utf-8') as file:
verification_data = json.load(file)
except:
verification_data = {}
# 添加新的验证码
expire_at = time.time() + expiry_time
verification_data[qq_number] = {
"code": verification_code,
"expire_at": expire_at
}
# 保存到文件
try:
with open(VERIFICATION_CACHE_FILE, 'w', encoding='utf-8') as file:
json.dump(verification_data, file, indent=2, ensure_ascii=False)
return True
except Exception as e:
print(f"保存验证码失败: {str(e)}")
return False
@staticmethod
def verify_code(qq_number, input_code):
"""
验证用户输入的验证码
参数:
qq_number (str): QQ号
input_code (str): 用户输入的验证码
返回:
bool: 验证成功返回True否则返回False
str: 成功或错误信息
"""
import time
# 检查缓存文件是否存在
if not os.path.exists(VERIFICATION_CACHE_FILE):
return False, "验证码不存在或已过期"
# 读取验证码数据
try:
with open(VERIFICATION_CACHE_FILE, 'r', encoding='utf-8') as file:
verification_data = json.load(file)
except:
return False, "验证码数据损坏"
# 检查该QQ号是否有验证码
if qq_number not in verification_data:
return False, "验证码不存在,请重新获取"
# 获取存储的验证码信息
code_info = verification_data[qq_number]
stored_code = code_info.get("code", "")
expire_at = code_info.get("expire_at", 0)
# 检查验证码是否过期
current_time = time.time()
if current_time > expire_at:
# 移除过期的验证码
del verification_data[qq_number]
with open(VERIFICATION_CACHE_FILE, 'w', encoding='utf-8') as file:
json.dump(verification_data, file, indent=2, ensure_ascii=False)
return False, "验证码已过期,请重新获取"
# 验证码比较(不区分大小写)
if input_code.upper() == stored_code.upper():
# 验证成功后移除该验证码
del verification_data[qq_number]
with open(VERIFICATION_CACHE_FILE, 'w', encoding='utf-8') as file:
json.dump(verification_data, file, indent=2, ensure_ascii=False)
return True, "验证码正确"
else:
return False, "验证码错误"
@staticmethod
def clean_expired_codes():
"""
清理过期的验证码
"""
import time
if not os.path.exists(VERIFICATION_CACHE_FILE):
return
try:
with open(VERIFICATION_CACHE_FILE, 'r', encoding='utf-8') as file:
verification_data = json.load(file)
current_time = time.time()
removed_keys = []
# 找出过期的验证码
for qq_number, code_info in verification_data.items():
expire_at = code_info.get("expire_at", 0)
if current_time > expire_at:
removed_keys.append(qq_number)
# 移除过期的验证码
for key in removed_keys:
del verification_data[key]
# 保存更新后的数据
with open(VERIFICATION_CACHE_FILE, 'w', encoding='utf-8') as file:
json.dump(verification_data, file, indent=2, ensure_ascii=False)
except Exception as e:
print(f"清理过期验证码失败: {str(e)}")
# 测试邮件发送
if __name__ == "__main__":
# 清理过期验证码
EmailVerification.clean_expired_codes()
# 生成验证码
test_qq = input("请输入测试QQ号: ")
verification_code = EmailVerification.generate_verification_code()
print(f"生成的验证码: {verification_code}")
# 发送测试邮件
success, message = EmailVerification.send_verification_email(test_qq, verification_code)
print(f"发送结果: {success}, 消息: {message}")
if success:
# 保存验证码
EmailVerification.save_verification_code(test_qq, verification_code)
# 测试验证
test_input = input("请输入收到的验证码: ")
verify_success, verify_message = EmailVerification.verify_code(test_qq, test_input)
print(f"验证结果: {verify_success}, 消息: {verify_message}")

BIN
Server/QQEmailSend.zip Normal file

Binary file not shown.

1519
Server/TCPGameServer.py Normal file

File diff suppressed because it is too large Load Diff

355
Server/TCPServer.py Normal file
View File

@@ -0,0 +1,355 @@
import socket
import threading
import json
import time
import sys
import logging
import colorama
from datetime import datetime
# 初始化colorama以支持跨平台彩色终端输出
colorama.init()
# 自定义日志格式化器,带有颜色和分类
class MinecraftStyleFormatter(logging.Formatter):
"""Minecraft风格的日志格式化器带有颜色和分类"""
# ANSI颜色代码
COLORS = {
'RESET': colorama.Fore.RESET,
'BLACK': colorama.Fore.BLACK,
'RED': colorama.Fore.RED,
'GREEN': colorama.Fore.GREEN,
'YELLOW': colorama.Fore.YELLOW,
'BLUE': colorama.Fore.BLUE,
'MAGENTA': colorama.Fore.MAGENTA,
'CYAN': colorama.Fore.CYAN,
'WHITE': colorama.Fore.WHITE,
'BRIGHT_BLACK': colorama.Fore.LIGHTBLACK_EX,
'BRIGHT_RED': colorama.Fore.LIGHTRED_EX,
'BRIGHT_GREEN': colorama.Fore.LIGHTGREEN_EX,
'BRIGHT_YELLOW': colorama.Fore.LIGHTYELLOW_EX,
'BRIGHT_BLUE': colorama.Fore.LIGHTBLUE_EX,
'BRIGHT_MAGENTA': colorama.Fore.LIGHTMAGENTA_EX,
'BRIGHT_CYAN': colorama.Fore.LIGHTCYAN_EX,
'BRIGHT_WHITE': colorama.Fore.LIGHTWHITE_EX,
}
# 日志级别颜色类似于Minecraft
LEVEL_COLORS = {
'DEBUG': COLORS['BRIGHT_BLACK'],
'INFO': COLORS['WHITE'],
'WARNING': COLORS['YELLOW'],
'ERROR': COLORS['RED'],
'CRITICAL': COLORS['BRIGHT_RED'],
}
# 类别及其颜色
CATEGORIES = {
'SERVER': COLORS['BRIGHT_CYAN'],
'NETWORK': COLORS['BRIGHT_GREEN'],
'CLIENT': COLORS['BRIGHT_YELLOW'],
'SYSTEM': COLORS['BRIGHT_MAGENTA'],
}
def format(self, record):
# 获取日志级别颜色
level_color = self.LEVEL_COLORS.get(record.levelname, self.COLORS['WHITE'])
# 从记录名称中确定类别默认为SERVER
category_name = getattr(record, 'category', 'SERVER')
category_color = self.CATEGORIES.get(category_name, self.COLORS['WHITE'])
# 格式化时间戳类似于Minecraft[HH:MM:SS]
timestamp = datetime.now().strftime('%H:%M:%S')
# 格式化消息
formatted_message = f"{self.COLORS['BRIGHT_BLACK']}[{timestamp}] {category_color}[{category_name}] {level_color}{record.levelname}: {record.getMessage()}{self.COLORS['RESET']}"
return formatted_message
class TCPServer:
def __init__(self, host='127.0.0.1', port=9000, buffer_size=4096):
"""初始化TCP服务器"""
self.host = host
self.port = port
self.buffer_size = buffer_size
self.socket = None
self.clients = {} # 存储客户端连接 {client_id: (socket, address)}
self.running = False
self.client_buffers = {} # 每个客户端的消息缓冲区
# 配置日志
self._setup_logging()
def _setup_logging(self):
"""设置Minecraft风格的日志系统"""
# 创建日志器
self.logger = logging.getLogger('TCPServer')
self.logger.setLevel(logging.INFO)
# 清除任何现有的处理器
if self.logger.handlers:
self.logger.handlers.clear()
# 创建控制台处理器
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
# 设置格式化器
formatter = MinecraftStyleFormatter()
console_handler.setFormatter(formatter)
# 添加处理器到日志器
self.logger.addHandler(console_handler)
def log(self, level, message, category='SERVER'):
"""使用指定的分类和级别记录日志"""
record = logging.LogRecord(
name=self.logger.name,
level=getattr(logging, level),
pathname='',
lineno=0,
msg=message,
args=(),
exc_info=None
)
record.category = category
self.logger.handle(record)
def start(self):
"""启动服务器"""
try:
# 创建TCP套接字
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) # 禁用Nagle算法
# 绑定地址和监听
self.socket.bind((self.host, self.port))
self.socket.listen(5)
self.running = True
self.log('INFO', f"服务器启动,监听 {self.host}:{self.port}", 'SERVER')
# 接受客户端连接的主循环
self._accept_clients()
except Exception as e:
self.log('ERROR', f"服务器启动错误: {e}", 'SYSTEM')
self.stop()
def _accept_clients(self):
"""接受客户端连接的循环"""
while self.running:
try:
# 接受新的客户端连接
client_socket, address = self.socket.accept()
client_id = f"{address[0]}:{address[1]}"
self.log('INFO', f"新客户端连接: {client_id}", 'NETWORK')
# 存储客户端信息
self.clients[client_id] = (client_socket, address)
self.client_buffers[client_id] = ""
# 创建处理线程
client_thread = threading.Thread(
target=self._handle_client,
args=(client_id,)
)
client_thread.daemon = True
client_thread.start()
# 通知客户端连接成功
self.send_data(client_id, {"type": "connection_status", "status": "connected"})
except KeyboardInterrupt:
self.log('INFO', "收到中断信号,服务器停止中...", 'SYSTEM')
break
except Exception as e:
self.log('ERROR', f"接受连接时出错: {e}", 'NETWORK')
time.sleep(1) # 避免CPU过度使用
def _handle_client(self, client_id):
"""处理客户端消息的线程"""
client_socket, _ = self.clients.get(client_id, (None, None))
if not client_socket:
return
# 设置超时,用于定期检查连接状态
client_socket.settimeout(30)
while self.running and client_id in self.clients:
try:
# 接收数据
data = client_socket.recv(self.buffer_size)
if not data:
# 客户端断开连接
self.log('INFO', f"客户端 {client_id} 断开连接", 'CLIENT')
self._remove_client(client_id)
break
# 处理接收的数据
self._process_data(client_id, data)
except socket.timeout:
# 发送保活消息
try:
self.send_data(client_id, {"type": "ping"})
except:
self.log('INFO', f"客户端 {client_id} 连接超时", 'CLIENT')
self._remove_client(client_id)
break
except Exception as e:
self.log('ERROR', f"处理客户端 {client_id} 数据时出错: {e}", 'CLIENT')
self._remove_client(client_id)
break
def _process_data(self, client_id, data):
"""处理从客户端接收的数据"""
# 将接收的字节添加到缓冲区
try:
decoded_data = data.decode('utf-8')
self.client_buffers[client_id] += decoded_data
# 处理可能包含多条JSON消息的缓冲区
self._process_buffer(client_id)
except UnicodeDecodeError as e:
self.log('ERROR', f"解码客户端 {client_id} 数据出错: {e}", 'CLIENT')
def _process_buffer(self, client_id):
"""处理客户端消息缓冲区"""
buffer = self.client_buffers.get(client_id, "")
# 按换行符分割消息
while '\n' in buffer:
message_end = buffer.find('\n')
message_text = buffer[:message_end].strip()
buffer = buffer[message_end + 1:]
# 处理非空消息
if message_text:
try:
# 解析JSON消息
message = json.loads(message_text)
self.log('INFO', f"从客户端 {client_id} 接收JSON: {message}", 'CLIENT')
# 处理消息 - 实现自定义逻辑
self._handle_message(client_id, message)
except json.JSONDecodeError:
# 非JSON格式作为原始文本处理
self.log('INFO', f"从客户端 {client_id} 接收文本: {message_text}", 'CLIENT')
self._handle_raw_message(client_id, message_text)
# 更新缓冲区
self.client_buffers[client_id] = buffer
def _handle_message(self, client_id, message):
"""处理JSON消息 - 可被子类覆盖以实现自定义逻辑"""
# 默认实现:简单回显
response = {
"type": "response",
"original": message,
"timestamp": time.time()
}
self.send_data(client_id, response)
def _handle_raw_message(self, client_id, message):
"""处理原始文本消息 - 可被子类覆盖以实现自定义逻辑"""
# 默认实现:简单回显
response = {
"type": "text_response",
"content": f"收到: {message}",
"timestamp": time.time()
}
self.send_data(client_id, response)
def send_data(self, client_id, data):
"""向指定客户端发送JSON数据"""
if client_id not in self.clients:
self.log('WARNING', f"客户端 {client_id} 不存在,无法发送数据", 'NETWORK')
return False
client_socket, _ = self.clients[client_id]
try:
# 转换为JSON字符串添加换行符
if isinstance(data, (dict, list)):
message = json.dumps(data) + '\n'
else:
message = str(data) + '\n'
# 发送数据
client_socket.sendall(message.encode('utf-8'))
return True
except Exception as e:
self.log('ERROR', f"向客户端 {client_id} 发送数据时出错: {e}", 'NETWORK')
self._remove_client(client_id)
return False
def broadcast(self, data, exclude=None):
"""向所有客户端广播消息,可选排除特定客户端"""
exclude = exclude or []
for client_id in list(self.clients.keys()):
if client_id not in exclude:
self.send_data(client_id, data)
def _remove_client(self, client_id):
"""断开并移除客户端连接"""
if client_id in self.clients:
client_socket, _ = self.clients[client_id]
try:
client_socket.close()
except:
pass
del self.clients[client_id]
if client_id in self.client_buffers:
del self.client_buffers[client_id]
self.log('INFO', f"客户端 {client_id} 已移除", 'CLIENT')
def stop(self):
"""停止服务器"""
self.running = False
# 关闭所有客户端连接
for client_id in list(self.clients.keys()):
self._remove_client(client_id)
# 关闭服务器套接字
if self.socket:
try:
self.socket.close()
except:
pass
self.log('INFO', "服务器已停止", 'SERVER')
# 使用示例
if __name__ == "__main__":
try:
# 创建并启动服务器
server = TCPServer()
# 以阻塞方式启动服务器
server_thread = threading.Thread(target=server.start)
server_thread.daemon = True
server_thread.start()
# 运行直到按Ctrl+C
print("服务器运行中按Ctrl+C停止...")
while True:
time.sleep(1)
except KeyboardInterrupt:
print("\n程序被用户中断")
if 'server' in locals():
server.stop()
sys.exit(0)

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,28 @@
{
"测试作物": {"花费": 1, "生长时间": 3, "收益": 9999, "品质": "普通", "描述": "测试作物", "耐候性": 10, "等级": 1, "经验": 999},
"小麦": {"花费": 120, "生长时间": 120, "收益": 100, "品质": "普通", "描述": "基础作物,品质较低,适合新手种植", "耐候性": 10, "等级": 1, "经验": 10},
"稻谷": {"花费": 100, "生长时间": 240, "收益": 120, "品质": "普通", "描述": "适合大规模种植的基础作物", "耐候性": 10, "等级": 1, "经验": 10},
"玉米": {"花费": 70, "生长时间": 600, "收益": 90, "品质": "普通", "描述": "营养丰富的优良作物,适合稍有经验的玩家", "耐候性": 15, "等级": 2, "经验": 15},
"土豆": {"花费": 75, "生长时间": 360, "收益": 90, "品质": "普通", "描述": "容易种植的耐寒作物", "耐候性": 12, "等级": 1, "经验": 10},
"胡萝卜": {"花费": 60, "生长时间": 480, "收益": 80, "品质": "普通", "描述": "适合新手的健康作物", "耐候性": 12, "等级": 1, "经验": 10},
"草莓": {"花费": 120, "生长时间": 960, "收益": 150, "品质": "优良", "描述": "营养丰富的果实,收益不错", "耐候性": 14, "等级": 2, "经验": 20},
"番茄": {"花费": 100, "生长时间": 720, "收益": 130, "品质": "优良", "描述": "常见作物,适合小规模种植", "耐候性": 12, "等级": 2, "经验": 15},
"大豆": {"花费": 90, "生长时间": 840, "收益": 110, "品质": "优良", "描述": "富含蛋白质的基础作物", "耐候性": 11, "等级": 2, "经验": 12},
"蓝莓": {"花费": 150, "生长时间": 1200, "收益": 200, "品质": "稀有", "描述": "较为稀有的作物,市场价值较高", "耐候性": 18, "等级": 3, "经验": 25},
"洋葱": {"花费": 85, "生长时间": 600, "收益": 105, "品质": "稀有", "描述": "烹饪常用的作物,适合中级种植", "耐候性": 10, "等级": 2, "经验": 10},
"南瓜": {"花费": 180, "生长时间": 1440, "收益": 250, "品质": "稀有", "描述": "秋季收获的高收益作物", "耐候性": 20, "等级": 4, "经验": 30},
"葡萄": {"花费": 200, "生长时间": 1200, "收益": 300, "品质": "稀有", "描述": "需要特殊管理的高收益作物", "耐候性": 15, "等级": 4, "经验": 35},
"柿子": {"花费": 160, "生长时间": 1080, "收益": 240, "品质": "稀有", "描述": "富含营养的秋季作物", "耐候性": 18, "等级": 3, "经验": 28},
"花椰菜": {"花费": 130, "生长时间": 960, "收益": 170, "品质": "稀有", "描述": "耐寒的高品质作物,适合经验丰富的玩家", "耐候性": 17, "等级": 3, "经验": 22},
"芦笋": {"花费": 200, "生长时间": 1560, "收益": 280, "品质": "稀有", "描述": "市场需求量高的稀有作物", "耐候性": 15, "等级": 4, "经验": 30},
"香草": {"花费": 250, "生长时间": 1800, "收益": 400, "品质": "史诗", "描述": "非常稀有且收益极高的作物", "耐候性": 22, "等级": 5, "经验": 40},
"西瓜": {"花费": 240, "生长时间": 2400, "收益": 420, "品质": "史诗", "描述": "夏季丰产的高价值作物", "耐候性": 21, "等级": 5, "经验": 45},
"甜菜": {"花费": 220, "生长时间": 2160, "收益": 350, "品质": "史诗", "描述": "营养丰富的根茎作物,收益较高", "耐候性": 20, "等级": 5, "经验": 38},
"甘蔗": {"花费": 260, "生长时间": 3000, "收益": 450, "品质": "史诗", "描述": "需要充足水源的高价值作物", "耐候性": 18, "等级": 5, "经验": 50},
"龙果": {"花费": 400, "生长时间": 4800, "收益": 600, "品质": "传奇", "描述": "极为稀有的热带作物,产量和价值都极高", "耐候性": 25, "等级": 6, "经验": 60},
"松露": {"花费": 500, "生长时间": 7200, "收益": 700, "品质": "传奇", "描述": "极其珍贵的地下作物,市场价格极高", "耐候性": 23, "等级": 7, "经验": 80},
"人参": {"花费": 450, "生长时间": 6600, "收益": 650, "品质": "传奇", "描述": "需要耐心等待的珍贵药材", "耐候性": 22, "等级": 6, "经验": 75},
"富贵竹": {"花费": 450, "生长时间": 6600, "收益": 650, "品质": "传奇", "描述": "需要耐心等待的珍贵药材", "耐候性": 22, "等级": 6, "经验": 75},
"芦荟": {"花费": 450, "生长时间": 6600, "收益": 650, "品质": "传奇", "描述": "需要耐心等待的珍贵药材", "耐候性": 22, "等级": 6, "经验": 75},
"金橘": {"花费": 420, "生长时间": 4800, "收益": 620, "品质": "传奇", "描述": "少见的耐寒果树,市场需求量极大", "耐候性": 26, "等级": 7, "经验": 70}
}

View File

@@ -0,0 +1,32 @@
{
"测试作物": {"花费": 1, "生长时间": 3, "收益": 9999, "品质": "普通", "描述": "测试作物", "耐候性": 10, "等级": 1, "经验": 999},
"小麦": {"花费": 120, "生长时间": 120, "收益": 100, "品质": "普通", "描述": "基础作物,品质较低,适合新手种植", "耐候性": 10, "等级": 1, "经验": 10},
"稻谷": {"花费": 100, "生长时间": 240, "收益": 120, "品质": "普通", "描述": "适合大规模种植的基础作物", "耐候性": 10, "等级": 1, "经验": 10},
"玉米": {"花费": 70, "生长时间": 600, "收益": 90, "品质": "普通", "描述": "营养丰富的优良作物,适合稍有经验的玩家", "耐候性": 15, "等级": 2, "经验": 15},
"土豆": {"花费": 75, "生长时间": 360, "收益": 90, "品质": "普通", "描述": "容易种植的耐寒作物", "耐候性": 12, "等级": 1, "经验": 10},
"胡萝卜": {"花费": 60, "生长时间": 480, "收益": 80, "品质": "普通", "描述": "适合新手的健康作物", "耐候性": 12, "等级": 1, "经验": 10},
"草莓": {"花费": 120, "生长时间": 960, "收益": 150, "品质": "优良", "描述": "营养丰富的果实,收益不错", "耐候性": 14, "等级": 2, "经验": 20},
"番茄": {"花费": 100, "生长时间": 720, "收益": 130, "品质": "优良", "描述": "常见作物,适合小规模种植", "耐候性": 12, "等级": 2, "经验": 15},
"大豆": {"花费": 90, "生长时间": 840, "收益": 110, "品质": "优良", "描述": "富含蛋白质的基础作物", "耐候性": 11, "等级": 2, "经验": 12},
"蓝莓": {"花费": 150, "生长时间": 1200, "收益": 200, "品质": "稀有", "描述": "较为稀有的作物,市场价值较高", "耐候性": 18, "等级": 3, "经验": 25},
"洋葱": {"花费": 85, "生长时间": 600, "收益": 105, "品质": "稀有", "描述": "烹饪常用的作物,适合中级种植", "耐候性": 10, "等级": 2, "经验": 10},
"南瓜": {"花费": 180, "生长时间": 1440, "收益": 250, "品质": "稀有", "描述": "秋季收获的高收益作物", "耐候性": 20, "等级": 4, "经验": 30},
"葡萄": {"花费": 200, "生长时间": 1200, "收益": 300, "品质": "稀有", "描述": "需要特殊管理的高收益作物", "耐候性": 15, "等级": 4, "经验": 35},
"柿子": {"花费": 160, "生长时间": 1080, "收益": 240, "品质": "稀有", "描述": "富含营养的秋季作物", "耐候性": 18, "等级": 3, "经验": 28},
"花椰菜": {"花费": 130, "生长时间": 960, "收益": 170, "品质": "稀有", "描述": "耐寒的高品质作物,适合经验丰富的玩家", "耐候性": 17, "等级": 3, "经验": 22},
"芦笋": {"花费": 200, "生长时间": 1560, "收益": 280, "品质": "稀有", "描述": "市场需求量高的稀有作物", "耐候性": 15, "等级": 4, "经验": 30},
"香草": {"花费": 250, "生长时间": 1800, "收益": 400, "品质": "史诗", "描述": "非常稀有且收益极高的作物", "耐候性": 22, "等级": 5, "经验": 40},
"西瓜": {"花费": 240, "生长时间": 2400, "收益": 420, "品质": "史诗", "描述": "夏季丰产的高价值作物", "耐候性": 21, "等级": 5, "经验": 45},
"甜菜": {"花费": 220, "生长时间": 2160, "收益": 350, "品质": "史诗", "描述": "营养丰富的根茎作物,收益较高", "耐候性": 20, "等级": 5, "经验": 38},
"甘蔗": {"花费": 260, "生长时间": 3000, "收益": 450, "品质": "史诗", "描述": "需要充足水源的高价值作物", "耐候性": 18, "等级": 5, "经验": 50},
"龙果": {"花费": 400, "生长时间": 4800, "收益": 600, "品质": "传奇", "描述": "极为稀有的热带作物,产量和价值都极高", "耐候性": 25, "等级": 6, "经验": 60},
"松露": {"花费": 500, "生长时间": 7200, "收益": 700, "品质": "传奇", "描述": "极其珍贵的地下作物,市场价格极高", "耐候性": 23, "等级": 7, "经验": 80},
"人参": {"花费": 450, "生长时间": 6600, "收益": 650, "品质": "传奇", "描述": "需要耐心等待的珍贵药材", "耐候性": 22, "等级": 6, "经验": 75},
"金橘": {"花费": 420, "生长时间": 4800, "收益": 620, "品质": "传奇", "描述": "少见的耐寒果树,市场需求量极大", "耐候性": 26, "等级": 7, "经验": 70}
}

View File

@@ -0,0 +1,64 @@
{
"experience": 0,
"level": 1,
"money": 1000,
"farm_name": "农场",
"user_name": "shumengya",
"player_name": "玩家昵称",
"user_password": "0123456789",
"last_login_time": "2025年12时09分35秒",
"total_login_time": "0时0分0秒",
"farm_lots": [
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": true, "is_planted": false, "max_grow_time": 3},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": true, "is_planted": false, "max_grow_time": 3},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": true, "is_planted": false, "max_grow_time": 3},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": true, "is_planted": false, "max_grow_time": 3},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": true, "is_planted": false, "max_grow_time": 3},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5},
{"crop_type": "", "grow_time": 0, "is_dead": false, "is_diged": false, "is_planted": false, "max_grow_time": 5}
],
"player_bag": []
}

View File

@@ -0,0 +1,6 @@
{
"1232132": {
"code": "5UFH2Z",
"expire_at": 1748005553.6155243
}
}

124
Server/deployment_guide.md Normal file
View File

@@ -0,0 +1,124 @@
# 萌芽农场游戏服务器部署指南
## 系统要求
- Python 3.7 或更高版本
- 稳定的互联网连接
- 建议2GB+ 内存,足够的磁盘空间存储玩家数据
## 安装步骤
### 1. 准备环境
```bash
# 在服务器上创建项目文件夹
mkdir MengYaFarm
cd MengYaFarm
# 克隆或上传服务器代码到此文件夹
# (手动上传文件或使用Git)
```
### 2. 安装依赖
```bash
# 创建虚拟环境(推荐)
python -m venv venv
# Linux/Mac激活虚拟环境
source venv/bin/activate
# Windows激活虚拟环境
# venv\Scripts\activate
# 安装依赖
pip install -r requirements.txt
```
### 3. 配置服务器
1. 确保已创建所需文件夹:
```bash
mkdir -p game_saves config
```
2. 创建初始玩家数据模板 (如果尚未存在):
```bash
# 在config目录中创建initial_player_data_template.json
```
3. 检查 TCPGameServer.py 中的服务器地址和端口配置:
```python
server_host: str = "0.0.0.0" # 使用0.0.0.0允许所有网络接口访问
server_port: int = 9000 # 确保此端口在防火墙中开放
```
4. 如需使用QQ邮箱验证功能请在QQEmailSend.py中更新发件邮箱配置:
```python
SENDER_EMAIL = 'your_qq_number@qq.com' # 发件人邮箱
SENDER_AUTH_CODE = 'your_auth_code' # 授权码
```
### 4. 启动服务器
```bash
# 直接启动
python Server/TCPGameServer.py
# 或使用nohup在后台运行
nohup python Server/TCPGameServer.py > server.log 2>&1 &
```
### 5. 监控与维护
- 服务器日志会输出到控制台或server.log
- 玩家数据存储在game_saves文件夹中
- 定期备份game_saves文件夹以防数据丢失
### 6. 防火墙配置
确保服务器防火墙允许TCP 9000端口的入站连接:
```bash
# Ubuntu/Debian
sudo ufw allow 9000/tcp
# CentOS/RHEL
sudo firewall-cmd --permanent --add-port=9000/tcp
sudo firewall-cmd --reload
```
### 7. 系统服务配置 (可选)
可以创建systemd服务使服务器自动启动:
```bash
# 创建服务文件
sudo nano /etc/systemd/system/mengyafarm.service
# 添加以下内容
[Unit]
Description=MengYa Farm Game Server
After=network.target
[Service]
Type=simple
User=your_username
WorkingDirectory=/path/to/MengYaFarm
ExecStart=/path/to/MengYaFarm/venv/bin/python /path/to/MengYaFarm/Server/TCPGameServer.py
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
# 启用并启动服务
sudo systemctl enable mengyafarm.service
sudo systemctl start mengyafarm.service
```
## 常见问题
### 服务器无法启动
- 检查Python版本
- 确认所有依赖已正确安装
- 检查端口是否被占用
### 客户端无法连接
- 确认服务器IP和端口配置正确
- 检查防火墙设置
- 验证网络连接
### 发送验证码失败
- 检查QQ邮箱和授权码设置
- 确认SMTP服务器可访问

View File

@@ -0,0 +1,414 @@
{
"experience": 0,
"level": 1,
"money": 1000,
"farm_name": "柚大青の小农场",
"player_name": "柚大青",
"user_name": "2143323382",
"user_password": "tyh@19900420",
"last_login_time": "2025年05月25日16时43分38秒",
"total_login_time": "0时0分29秒",
"farm_lots": [
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 3
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 3
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 3
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 3
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 3
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
}
],
"player_bag": []
}

View File

@@ -0,0 +1,370 @@
{
"experience": 508,
"farm_lots": [
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 3
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 3
},
{
"crop_type": "花椰菜",
"grow_time": 960,
"is_dead": false,
"is_diged": true,
"is_planted": true,
"max_grow_time": 960
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 3
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 3
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 3
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 3
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 3
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 4800
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 1200
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 4800
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 2400
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 3
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 3
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 120
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 720
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 2400
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 1200
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 1800
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 600
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 480
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 960
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 960
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": false,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 5
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 2400
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 3
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 3
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 960
},
{
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 960
}
],
"player_bag": [
{
"count": 18,
"name": "测试作物",
"quality": "普通"
},
{
"count": 253,
"name": "金橘",
"quality": "传奇"
},
{
"name": "胡萝卜",
"quality": "普通",
"count": 17
},
{
"name": "花椰菜",
"quality": "稀有",
"count": 11
},
{
"name": "芦笋",
"quality": "稀有",
"count": 8
},
{
"name": "草莓",
"quality": "优良",
"count": 4
},
{
"name": "香草",
"quality": "史诗",
"count": 3
}
],
"farm_name": "树萌芽の大农场",
"player_name": "树萌芽",
"level": 36,
"money": 40013,
"last_login_time": "2025年05月24日11时51分35秒",
"total_login_time": "100时33分38秒",
"user_name": "3205788256",
"user_password": "tyh@19900420"
}

4
Server/requirements.txt Normal file
View File

@@ -0,0 +1,4 @@
# Game Server Dependencies
colorama>=0.4.6 # For colored terminal output
# Email Requirements
# Standard library dependencies are not listed (socket, threading, json, etc.)