- 精华
- 阅读权限
- 40
- 贡献
- 人
- 好友
- 相册
- 分享
- 听众
- 收听
- 注册时间
- 2021-5-26
- 在线时间
- 小时
- 最后登录
- 1970-1-1
|
发表于 2026-6-24 00:22:27
|
显示全部楼层
# 傲视天地 (ASTD) 服务端修复攻略 — 完整版
> 把本文档喂给任何 AI,让它逐步修复残端直到服务器成功启动。
---
## 一、环境
| 组件 | 路径/版本 |
|------|-----------|
| Tomcat | `D:\新建文件夹\astd3` (Tomcat 6.0.20,已解压好) |
| Java | `C:\Program Files\Eclipse Adoptium\jdk-8.0.492.9-hotspot` (Java 8) |
| MySQL | `D:\mysql-5.7.44-winx64` (5.7.44 便携版) |
| Python | `D:\anaconda3\python.exe` (或任意 Python 3) |
| 游戏 JAR | `D:\新建文件夹\astd3\webapps\root\WEB-INF\lib\astd.jar` (主逻辑 2245 类) |
| Flash 客户端 | `D:\新建文件夹\astd3\webapps\astd\` (Main.swf + 85 模块 SWF) |
**运行端口**: Tomcat HTTP=12082, AJP=12011, MySQL=3306, Python HTTP=8888
---
## 二、前置:文件整合
从泄漏的 `客户/` 目录复制这些文件到对应位置(如果还没有的话):
```
客户/8.6/ft/astd.jar → webapps/root/WEB-INF/lib/astd.jar
客户/8.6/kf_comm.jar → webapps/root/WEB-INF/lib/kf_comm.jar
客户/8.6/astdcore.jar → webapps/root/WEB-INF/lib/astdcore.jar
客户/8.6/bs_comm.jar → webapps/root/WEB-INF/lib/bs_comm.jar
客户/astd/ → webapps/astd/ (整个目录)
```
**备份原则**: 修改任何文件前先备份。本文假定备份已完成。
---
## 三、修复清单(共 14 步)
### 第 1 步:修补 Spring JdkVersion(Java 8 兼容)
**问题**: Spring 2.5 的 `JdkVersion.class` 不识别 Java 8 的 `1.8.0` 版本号。
**修复**: 创建 `lib/00-jdk-fix.jar`
```bash
# 1. 解压 spring.jar(在 D:\新建文件夹\astd3\lib 目录)
cd "D:\新建文件夹\astd3\lib"
jar xf spring.jar org/springframework/core/JdkVersion.class
# 2. 用 Python 修补字节码
D:\anaconda3\python.exe << 'EOF'
with open("org/springframework/core/JdkVersion.class", "rb") as f:
data = bytearray(f.read())
# 偏移 0x4C7: 把 fallback 值从 iconst_1 (JAVA_14) 改为 iconst_4 (JAVA_17)
# 0x04 = iconst_1, 0x07 = iconst_4 → 改为 0x07
data[0x4C7] = 0x07
with open("org/springframework/core/JdkVersion.class", "wb") as f:
f.write(data)
EOF
# 3. 打包为 00-jdk-fix.jar(00- 前缀确保字母序在 spring.jar 之前)
jar cf 00-jdk-fix.jar org/
# 4. 清理临时目录
rm -rf org/
```
---
### 第 2 步:修改 web.xml
**文件**: `D:\新建文件夹\astd3\webapps\root\WEB-INF\web.xml`
**修改内容**(必须和下面一致):
```xml
<!-- context-param 部分 -->
<context-param>
<param-name>com.xinyun.astdcore</param-name>
<param-value>http://127.0.0.1:8888/proxool.xml</param-value>
</context-param>
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.XmlWebApplicationContext</param-value>
</context-param>
<!-- 重新启用 JarLoadListener -->
<listener>
<listener-class>com.xinyun.listener.JarLoadListener</listener-class>
</listener>
```
> **要点**: `contextClass` 必须是标准 `XmlWebApplicationContext`,不能用自定义的 `MyXmlWebApplicationContext`(后者在 Java 8 上会崩溃)。
---
### 第 3 步:修改 common-applicationContext.xml
**文件**: `D:\新建文件夹\astd3\webapps\root\WEB-INF\common-applicationContext.xml`
**修改 3 处**:
**(a) 数据库连接 URL(第 18 行)**——改成:
```xml
<property name="driverUrl" value="jdbc:mysql://127.0.0.1:3306/astd?user=root&password=123456&characterEncoding=utf8&useSSL=false&zeroDateTimeBehavior=convertToNull"></property>
```
**(b) Hibernate 映射 JAR(第 50 行)**——改成:
```xml
<property name="mappingJarLocations">
<list>
<value>astd-res.jar</value>
</list>
</property>
```
**(c) 注释掉以下三个标签**(Java 8 + Spring 2.5 不兼容):
```xml
<!-- <tx:annotation-driven transaction-manager="transactionManager" /> -->
<!-- <context:annotation-config/> -->
<!-- <context:component-scan base-package="com.xinyun"/> -->
```
---
### 第 4 步:修改 server.properties
**文件**: `D:\新建文件夹\astd3\webapps\root\WEB-INF\server.properties`
**找到并修改**:
```properties
SDATA_URL=http://127.0.0.1:8888/sdata.zip
```
---
### 第 5 步:创建 astd-res.jar(Hibernate 映射文件)
**问题**: `common-applicationContext.xml` 引用 `astd-res.jar` 作为 Hibernate 映射源。`MyLocalSessionFactoryBean` 重写了 `setMappingJarLocations()`,通过 `ClassLoader.getResourceAsStream()` 从 classpath 查找该 JAR。
`astd.jar` 中包含了全部 328 个 `.hbm.xml` 文件(位于 `hbmxml/` 路径下),只需将其提取并打包为独立的 `astd-res.jar`。
```bash
cd "D:\新建文件夹\astd3"
rm -rf _tmp_hbm && mkdir _tmp_hbm && cd _tmp_hbm
"C:\Program Files\Eclipse Adoptium\jdk-8.0.492.9-hotspot\bin\jar.exe" xf "../webapps/root/WEB-INF/lib/astd.jar" hbmxml/
"C:\Program Files\Eclipse Adoptium\jdk-8.0.492.9-hotspot\bin\jar.exe" cf "../webapps/root/WEB-INF/classes/astd-res.jar" hbmxml/
cd .. && rm -rf _tmp_hbm
```
> **要点**: `astd-res.jar` 必须放到 `WEB-INF/classes/` 目录,因为 `ClassLoader.getResourceAsStream("astd-res.jar")` 搜索的是 classpath 路径,而 `WEB-INF/classes/` 是 Tomcat classpath 的根。
---
### 第 6 步:创建 C:\Tomcat 6.0\temp\ 目录
代码中 `ListenerConstants.WEB_PATH` 硬编码指向这个路径。
```cmd
mkdir "C:\Tomcat 6.0\temp"
copy "D:\新建文件夹\astd3\webapps\root\WEB-INF\applicationContext.xml" "C:\Tomcat 6.0\temp\"
copy "D:\新建文件夹\astd3\webapps\root\WEB-INF\common-applicationContext.xml" "C:\Tomcat 6.0\temp\"
copy "D:\新建文件夹\astd3\webapps\root\WEB-INF\server.properties" "C:\Tomcat 6.0\temp\"
```
---
### 第 7 步:配置 MySQL
**文件**: `D:\mysql-5.7.44-winx64\my.ini`
```ini
[mysqld]
basedir=D:/mysql-5.7.44-winx64
datadir=D:/mysql-5.7.44-winx64/data
port=3306
character-set-server=utf8
default-storage-engine=INNODB
explicit_defaults_for_timestamp=1
sql_mode=NO_ENGINE_SUBSTITUTION
[client]
default-character-set=utf8
```
**初始化 MySQL**(如果 data 目录还没初始化):
```cmd
D:\mysql-5.7.44-winx64\bin\mysqld.exe --defaults-file=D:\mysql-5.7.44-winx64\my.ini --initialize-insecure
```
**启动 MySQL**:
```cmd
D:\mysql-5.7.44-winx64\bin\mysqld.exe --defaults-file=D:\mysql-5.7.44-winx64\my.ini --console
```
**设置 root 密码**(首次启动后,新开终端):
```cmd
D:\mysql-5.7.44-winx64\bin\mysql.exe -u root --host=127.0.0.1 --protocol=TCP --skip-password -e "ALTER USER 'root'@'localhost' IDENTIFIED BY '123456'; FLUSH PRIVILEGES;"
```
---
### 第 8 步:创建数据库并导入表结构
```cmd
:: 创建数据库
D:\mysql-5.7.44-winx64\bin\mysql.exe -u root -p123456 --host=127.0.0.1 --protocol=TCP -e "CREATE DATABASE IF NOT EXISTS astd DEFAULT CHARSET utf8;"
:: 导入 324 张表
D:\mysql-5.7.44-winx64\bin\mysql.exe -u root -p123456 --host=127.0.0.1 --protocol=TCP astd < D:\新建文件夹\astd3\schema.sql
```
---
### 第 9 步:导入游戏初始数据(sdata)
**准备**: 确保 `D:\新建文件夹\astd3\bin\sdata.zip` 存在(包含 `sdata8.62.xml`)。
**Python 导入脚本** — 创建 `D:\新建文件夹\astd3\import_sdata.py`:
```python
"""Parse sdata XML and import to MySQL. Handles empty datetime values."""
import zipfile, xml.etree.ElementTree as ET, subprocess
MYSQL = r"D:\mysql-5.7.44-winx64\bin\mysql.exe"
DB, USER, PASS = "astd", "root", "123456"
def get_cols(table):
r = subprocess.run([MYSQL, "-u", USER, f"-p{PASS}", "--host=127.0.0.1", "--protocol=TCP", DB,
"-e", f"SHOW COLUMNS FROM `{table}`"], capture_output=True, text=True)
cols = {}
if r.returncode == 0:
for line in r.stdout.strip().split('\n')[1:]:
p = line.split('\t')
if p:
name = p[0].strip()
dtype = p[1].strip() if len(p) > 1 else "varchar"
cols[name.lower()] = (name, dtype)
return cols
def is_temporal(dtype):
t = dtype.lower()
return t.startswith(('datetime', 'timestamp', 'date', 'time', 'year'))
with zipfile.ZipFile(r"D:\新建文件夹\astd3\bin\sdata.zip") as zf:
xml = zf.read(zf.namelist()[0])
root = ET.fromstring(xml)
all_sql, total, skipped_tables, null_fixes = [], 0, 0, 0
for tbl in root.findall(".//table"):
name = tbl.get("name")
rows = tbl.findall("row")
if not rows:
continue
db_cols = get_cols(name)
if not db_cols:
skipped_tables += 1
continue
xml_fields = set()
for row in rows:
for f in row.findall("field"):
xml_fields.add(f.get("name"))
matched = [(f, db_cols[f.lower()][0], db_cols[f.lower()][1])
for f in xml_fields if f.lower() in db_cols]
if not matched:
skipped_tables += 1
continue
tbl_null_fixes = 0
vals_list = []
for row in rows:
vals = []
for xf, db_name, db_type in matched:
elem = row.find(f"field[@name='{xf}']")
v = elem.text if elem is not None and elem.text else ""
v = v.replace("\\", "\\\\").replace("'", "''").replace("\x00", "")
if v == "" and is_temporal(db_type):
vals.append("NULL")
tbl_null_fixes += 1
else:
vals.append(f"'{v}'")
vals_list.append(f"({', '.join(vals)})")
null_fixes += tbl_null_fixes
if tbl_null_fixes > 0:
print(f" {name}: {tbl_null_fixes} empty datetime → NULL fixes")
for i in range(0, len(vals_list), 200):
batch = vals_list[i:i+200]
col_names = ', '.join('`{}`'.format(c[1]) for c in matched)
sql = f"INSERT IGNORE INTO `{name}` ({col_names}) VALUES {', '.join(batch)};"
all_sql.append(sql)
total += len(batch)
if len(db_cols) != len(xml_fields):
print(f" {name}: {len(rows)} rows (DB:{len(db_cols)} XML:{len(xml_fields)})")
print(f"\n{len(all_sql)} stmts, {total} rows, {null_fixes} datetime fixes, {skipped_tables} skipped")
sql_file = r"D:\新建文件夹\astd3\sdata_import.sql"
with open(sql_file, 'w', encoding='utf-8') as f:
f.write("USE astd;\n" + "\n".join(all_sql) + "\n")
print("Importing...")
r = subprocess.run(
f'"{MYSQL}" -u {USER} -p{PASS} --host=127.0.0.1 --protocol=TCP --default-character-set=utf8 {DB} < "{sql_file}"',
shell=True, capture_output=True, text=True)
if r.returncode == 0:
print("OK!")
else:
lines = [l for l in r.stderr.split('\n') if 'Using a password' not in l and l.strip()]
for l in lines[:10]:
print(l)
```
**运行导入**:
```cmd
D:\anaconda3\python.exe D:\新建文件夹\astd3\import_sdata.py
```
---
### 第 10 步:替换 MySQL JDBC 驱动 ⭐关键步骤
**问题**: 原驱动 `mysql-connector-java-5.0.4-bin.jar` (2007年) 在每次连接时强制设置 `STRICT_TRANS_TABLES`,导致空字符串 datetime 被 MySQL 5.7 拒绝。
**修复**: 更换为 `mysql-connector-java-5.1.49.jar`(2021年最后版本,兼容 Java 8 + MySQL 5.7)。
**下载**:
```cmd
curl -L -o "D:\新建文件夹\astd3\lib\mysql-connector-java-5.1.49.jar" "https://repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.49/mysql-connector-java-5.1.49.jar"
```
**替换**(确认 Tomcat 已停止):
```cmd
del "D:\新建文件夹\astd3\lib\mysql-connector-java-5.0.4-bin.jar"
del "D:\新建文件夹\astd3\lib\mysql-connector-java-5.0.4-bin.jar.bak"
:: 确保 WEB-INF/lib 下也没有旧驱动
del "D:\新建文件夹\astd3\webapps\root\WEB-INF\lib\mysql-connector*"
```
> ⚠️ 确保全局只有一个 MySQL 驱动 JAR:`mysql-connector-java-5.1.49.jar`
---
### 第 11 步:修复数据库列类型
`player_job` 表的 `EXECUTION_TIME` 列是 `DATETIME`,但 Java 代码中该字段是 `java.lang.Long`(毫秒时间戳)。需要改成 `BIGINT`。
```cmd
D:\mysql-5.7.44-winx64\bin\mysql.exe -u root -p123456 --host=127.0.0.1 --protocol=TCP astd -e "ALTER TABLE player_job MODIFY COLUMN EXECUTION_TIME BIGINT(20) DEFAULT NULL;"
```
> 同时更新 `schema.sql` 中对应行:`EXECUTION_TIME DATETIME` → `EXECUTION_TIME BIGINT`
---
### 第 12 步:插入必需的初始数据
以下表需要手动插入初始数据才能让游戏启动通过 `IniData` 初始化:
```sql
-- 1. system_attribute(游戏系统属性,8 个必需 ID)
INSERT INTO system_attribute (ID, NAME, VALUE, GIFT_DATE) VALUES
(1, 'GIFT_SYSTEM_ATTRIBUTE', '0', NOW()),
(2, 'DAY_GIFT_SYSTEM_ATTRIBUTE', '0', NOW()),
(3, 'NEWAREA_OPEN_SYSTEM_ATTRIBUTE', '0', NOW()),
(4, 'REPAY_EVENT_LAST_PAYSUM', '0', NOW()),
(5, 'REPAY_EVENT_NEEDPAY', '0', NOW()),
(6, 'REPAY_EVENT_STATE', '0', NOW()),
(7, 'NEWAREA_BOWLDRES_CONT', '0', NOW()),
(8, 'DEV_ZONE', '1', NOW());
-- 2. version_control(版本检查,必须和 JAR 版本匹配)
-- astd.jar 版本: 8.6.2.1--14/01/27
-- astdcore.jar 版本: 1.8.1.5
INSERT INTO version_control (id, version, core_version) VALUES (1, '8.6.2.1--14/01/270', '1.8.1.5');
-- 3. nation_miracle(三国奇迹数据)
INSERT INTO nation_miracle (nation, date, num, get_reward) VALUES
(1, NOW(), '0', '0'),
(2, NOW(), '0', '0'),
(3, NOW(), '0', '0');
-- 4. stock_ratio(股票比率,空表会导致 IndexOutOfBoundsException)
INSERT INTO stock_ratio (ID) VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
```
---
### 第 13 步:修改 Flash 客户端入口
**文件**: `D:\新建文件夹\astd3\webapps\astd\index.html`
**第 162 行**,把:
```javascript
loadVersion("http://127.0.0.1/astd/");
```
改为:
```javascript
loadVersion("http://127.0.0.1:12082/astd/");
```
> 原 URL 没有端口号,Flash 请求会默认走 80 端口导致 `ERR_CONNECTION_REFUSED`。
---
### 第 14 步:修改 proxool.xml(可选,不需要改)
`D:\新建文件夹\astd3\bin\proxool.xml` 文件**不能删除**(JarLoadListener 需要通过 Python HTTP 提供它),但 `ProxoolStartup` 类会在启动时动态注入 `driverUrl`,所以 proxool.xml 中不需要写 `<driver-url>`。
当前文件内容(保持不变):
```xml
<?xml version="1.0" encoding="UTF-8"?>
<proxool-config>
<proxool>
<alias>astd</alias>
<driver-class>com.mysql.jdbc.Driver</driver-class>
<driver-properties>
<property name="user" value="root" />
<property name="password" value="123456" />
</driver-properties>
<house-keeping-sleep-time>100000</house-keeping-sleep-time>
<prototype-count>20</prototype-count>
<simultaneous-build-throttle>20</simultaneous-build-throttle>
<maximum-connection-count>400</maximum-connection-count>
<minimum-connection-count>20</minimum-connection-count>
<maximum-new-connections>50</maximum-new-connections>
<maximum-active-time>600000</maximum-active-time>
<test-before-use>true</test-before-use>
<house-keeping-test-sql>select CURRENT_DATE</house-keeping-test-sql>
</proxool>
</proxool-config>
```
---
## 四、启动顺序
每次启动按这个顺序:
**终端 1 — MySQL**:
```cmd
D:\mysql-5.7.44-winx64\bin\mysqld.exe --defaults-file=D:\mysql-5.7.44-winx64\my.ini --console
```
**终端 2 — Python HTTP 服务器**(提供 proxool.xml 和 sdata.zip):
```cmd
D:\anaconda3\python.exe -m http.server 8888 -d D:\新建文件夹\astd3\bin
```
**终端 3 — Tomcat**:
```cmd
set JAVA_HOME=C:\Program Files\Eclipse Adoptium\jdk-8.0.492.9-hotspot
set CATALINA_HOME=D:\新建文件夹\astd3
D:\新建文件夹\astd3\bin\catalina.bat run
```
> 使用 `catalina.bat run`(前台模式)可以看到所有控制台输出,方便调试。
**启动成功标志**:
- 控制台出现 `Server startup in XXXX ms`
- `D:\新建文件夹\astd3\logs\Game\Common\Game.log` 中出现 `Scheduler DefaultQuartzScheduler started`
- 不出现 `Shutting down` 或 `System.exit`
**访问游戏**:
- 浏览器打开 `http://127.0.0.1:12082/astd/`
- 需要一个支持 Flash 的浏览器(或安装 Ruffle 扩展:https://ruffle.rs/)
---
## 五、常见问题排查
### Q: Spring 启动时报 `Incorrect datetime value: '' for column 'uptime'`
→ 检查第 10 步 MySQL 驱动是否已替换为 5.1.49,以及 `my.ini` 中 `sql_mode=NO_ENGINE_SUBSTITUTION`。
### Q: Tomcat 启动后立即 Shutdown(JVM 静默退出)
→ 查看 `D:\新建文件夹\astd3\logs\Game\Common\Game.log` 中的 `[ERROR]` 行。`IniData` 初始化 26 个服务时任何失败都会调用 `System.exit(1)`。
常见缺失数据:
- `ObjectNotFoundException: NationMiracle#X` → 补 nation_miracle 行
- `ObjectNotFoundException: VersionControl#1` → 补 version_control 行
- `PropertyAccessException: Null value for primitive type` → 补全 NOT NULL 列的默认值
### Q: 浏览器报 `ERR_CONNECTION_REFUSED`
→ 检查是否用了端口 `:12082`(默认 80 不对)。确认 `index.html` 中 `loadVersion` 的 URL 包含端口号。
### Q: Flash 加载后黑屏/无法连接
→ 查看 Tomcat 控制台输出中的异常。Flash 通过 `*.action` URL 与后端通信,未正确登录时会重定向。
---
## 六、修复原理速查
| # | 问题 | 根因 | 修复 |
|---|------|------|------|
| 1 | JdkVersion | Spring 2.5 不认 Java 8 版本字符串 | 字节码补丁 fallback 值 |
| 2 | MyXmlWebApplicationContext | 自定义 Spring Context 在 Java 8 上生成 component.xml 时静默崩溃 | 换标准 XmlWebApplicationContext |
| 3 | astd-res.jar 缺失 | hbm.xml 在 astd.jar 内,但配置要求独立 JAR | 提取 hbmxml/ 打包为 astd-res.jar |
| 4 | MySQL STRICT 模式 | Connector/J 5.0.4 强制添加 STRICT_TRANS_TABLES | 升级到 5.1.49 + sql_mode=NO_ENGINE_SUBSTITUTION |
| 5 | EXECUTION_TIME | Java Long 类型对不上 DB DATETIME | ALTER 改为 BIGINT |
| 6 | system_attribute 空表 | Bean init 时 read() 返回 null → NPE | 插入 8 行系统属性 |
| 7 | version_control 空表 | 版本检查失败 throw Exception | 插入匹配版本号的数据 |
| 8 | nation_miracle 空表 | INationService.init() 查询不存在 | 插入三国数据 |
| 9 | index.html 端口 | 硬编码 URL 不带 12082 端口 | 加上 :12082 |
| 10 | 存储过程返回值 | 返回 `'ok'`,Java `getInt()` 报错 | 全部改为返回 `'0'` |
| 11 | 字符过滤禁语 | 用户名含 "GM" 被拦截 | 清空 illegalkeyword.txt |
| 12 | player_general NULL int | `LEADER`/`FORCES`/`INTELLIGENCE` 等 NULL 撞 Java 原生类型 | DEFAULT 0 |
| 13 | 表重复行 | INSERT IGNORE 无效因为没有 UNIQUE 约束 | 加 UNIQUE KEY |
| 14 | player_equip.GENERAL_ID | 引用了 sdata general.ID 而非 player_general.ID | 文档标注 |
| 15 | 阵型 FORMATION_ID | 与科技升级后的阵型 ID 不匹配 | 重置 FORMATION_ID |
| 16 | 教程 XINSHOU_TASK | 设为 '1' 导致 ArrayIndexOutOfBounds | 设 NULL |
## 七、运行时数据库修复(持续更新)
> 这些是游戏运行过程中遇到的 DB 类型/数据问题。**规律:schema.sql 由 hbm.xml 盲猜生成,Java 实际类型不匹配。**
### 类型修复铁律
| Java 实际类型 | schema.sql 猜的类型 | 典型报错 | 正确类型 |
|-------------|-------------------|---------|---------|
| long/Integer | datetime | `Incorrect datetime value: '17...'` | BIGINT |
| String | int/bigint | `Incorrect integer value: 'G'` | VARCHAR |
| int | datetime | `Incorrect datetime value: '1'` | BIGINT/INT |
| java.util.Date | bigint | `Data truncated for column` | DATETIME |
| Integer | (NULL值) | `NullPointerException` `.intValue()` | DEFAULT 0 |
### 已修复列表(共20+项)
```sql
-- 第11步补遗:毫秒时间戳 → bigint(原来只修了 EXECUTION_TIME)
ALTER TABLE storehouse MODIFY COLUMN intime BIGINT;
ALTER TABLE storehouse_baoshi MODIFY COLUMN intime BIGINT;
ALTER TABLE player_crystal MODIFY COLUMN intime BIGINT;
ALTER TABLE constructor MODIFY COLUMN WORKINGTIME BIGINT;
ALTER TABLE refiner_group MODIFY COLUMN WORKINGTIME BIGINT;
ALTER TABLE player_attribute MODIFY COLUMN IMPOSETIME BIGINT;
ALTER TABLE player_attribute MODIFY COLUMN LASTSAVESALARYTIME BIGINT;
ALTER TABLE resource MODIFY COLUMN START_TIME BIGINT;
ALTER TABLE resource MODIFY COLUMN UPDATE_TIME BIGINT;
ALTER TABLE resource MODIFY COLUMN TIME_INTERVAL BIGINT;
-- String → varchar(Java 写字符串到 int 列)
ALTER TABLE player MODIFY COLUMN FLAG VARCHAR(255);
ALTER TABLE player_unique MODIFY COLUMN unique_id VARCHAR(255);
ALTER TABLE battle_report_record MODIFY COLUMN REPORT_ID VARCHAR(255);
ALTER TABLE challenge_battle_report_record MODIFY COLUMN REPORT_ID VARCHAR(255);
ALTER TABLE epic_battle_report_record MODIFY COLUMN REPORT_ID VARCHAR(255);
ALTER TABLE player_combo_report MODIFY COLUMN BATTLE_ID VARCHAR(255);
ALTER TABLE player_attribute2 MODIFY COLUMN CHANGE_ATT_TIME VARCHAR(255);
ALTER TABLE player_attribute2 MODIFY COLUMN ATT_NUM_PER_DAY VARCHAR(255);
ALTER TABLE NATION_OFFICER MODIFY COLUMN BATTLE_ID VARCHAR(255);
ALTER TABLE player_stock MODIFY COLUMN STOCK_NUM VARCHAR(255);
ALTER TABLE player_stock MODIFY COLUMN BUY_PRICE VARCHAR(255);
ALTER TABLE BOWLDERRES MODIFY COLUMN flag VARCHAR(255);
ALTER TABLE Player_Pick MODIFY COLUMN QUEUE_WORKER_NUMS VARCHAR(255);
ALTER TABLE Player_Pick MODIFY COLUMN QUEUE_LEVELS VARCHAR(255);
-- int → bigint(Java 写毫秒时间戳到 datetime)
ALTER TABLE player_battle_event MODIFY COLUMN done_times BIGINT;
ALTER TABLE player_city_event MODIFY COLUMN done_times BIGINT;
ALTER TABLE player_weaver_info MODIFY COLUMN totalweavetime BIGINT;
ALTER TABLE nation MODIFY COLUMN CANCEL_NATION_TASK_TIME BIGINT;
ALTER TABLE player_dinner MODIFY COLUMN take_times BIGINT;
-- datetime → int(Java int 写到 datetime)
ALTER TABLE nation MODIFY COLUMN DINNER_TIMES INT;
ALTER TABLE player_fete MODIFY COLUMN copper_times INT;
ALTER TABLE player_fete MODIFY COLUMN gem_times INT;
ALTER TABLE player_fete MODIFY COLUMN bowlder_times INT;
-- (player_fete 共14个 *_times 列全改 INT)
ALTER TABLE player_baowu MODIFY COLUMN polish_times INT;
ALTER TABLE player_newarea_attribute MODIFY COLUMN refreshtokentimes INT;
ALTER TABLE player_newarea_attribute MODIFY COLUMN passapplytime INT;
ALTER TABLE player_newarea_attribute MODIFY COLUMN goldrefreshtokentime INT;
ALTER TABLE player_newarea_attribute MODIFY COLUMN goldRefreshOneTokenTimes INT;
-- bigint → datetime(盲改错了,Java 是 Date!恢复)
ALTER TABLE nation MODIFY COLUMN LAST_FINISH_NATION_TASK_TIME DATETIME;
ALTER TABLE nation MODIFY COLUMN LAST_REFRESH_TIME DATETIME;
ALTER TABLE nation MODIFY COLUMN nation_task_end_time DATETIME;
ALTER TABLE nation MODIFY COLUMN mass_end_time DATETIME;
-- NULL → 默认值(Integer.intValue() NPE)
UPDATE player_newarea_attribute SET treasurenum=0, consumeCityHp=0 WHERE treasurenum IS NULL;
UPDATE player_newarea_attribute SET refinerList='', refreshOneRefinerNum=0, refreshRefinersNum=0;
UPDATE npc_info SET protect_time='2020-01-01 00:00:00' WHERE protect_time IS NULL;
```
### 错误定位技巧
1. 查 Game.log:`grep "ERROR" logs/Game/Common/Game.log | tail -5`
2. NPE 无堆栈 → JVM 已优化掉,重启 Tomcat(catalina.bat 已加 `-XX:-OmitStackTraceInFastThrow`)
3. `Data truncation` / `Incorrect xxx value` → 查声明式 Java 类型(反编译 class)再改列
4. **改列前必须先确认 Java 类型!** 从 astd.jar 用 javap 反编译:`javap -p com/xinyun/xxx/domain/Xxx.class`
---
## 八、游戏系统发现
### 宝石系统
| | 棱彩石 (≤220级) | 水晶石 (>220级) |
|------|-----------|-----------|
| goods_id | 1000001 | 1000002 |
| BAOSHI_STORE_ID | **直接=宝石等级**(1-20) | **100 + player_crystal.ID** |
| 配置表 | `baoshi` 表 | `crystal` 表 |
| 代码查询 | `baoshiCache.get(level)` | `playerCrystalDao.load(id)` |
> BAOSHI_STORE_ID2 同规则。第二孔 ≤180 级为 -1(未开),≥180 可开。
### 宝物系统
完整链路:`player_baowu` → `storehouse(goodsId=2000001, enchanting=baowu.ID)` → `player_equip(EQUIP_TYPE=7)` → 武将
| 品质 | quality_value | 最低 property_base |
|------|-------------|-------------------|
| 家传玉佩 | 0 | 0 |
| 镇国宝玉 | 15 | 150000 |
| 南洋翠玉 | 19 | 190000 |
| 海洋之星 | 23 | 230000 |
| 日月光华 | 30 | 300000 |
| 无双神器 | 40 | 400000 |
> `polish_times` DB 列是 datetime 但 Java 是 int,已修复。
### 兵器系统
- goodsId < 100000 = 装备,goodsId 100000~200000 = 兵器
- 紫色兵器: 1X6010 (X=1-7),即 116010~176010
- 坚毅度上限: `troopequipdetail.rigidity_limit`,满级 150 = 20250
- 魔化: `storehouse.enchanting`,仅装备
### 活动系统
```sql
-- 激活全部活动
INSERT INTO curr_event (eventId, start, end, eventId2)
SELECT ID, UNIX_TIMESTAMP(NOW())*1000, UNIX_TIMESTAMP(DATE_ADD(NOW(), INTERVAL 7 DAY))*1000, ID FROM event;
```
- 春节活动需在 `server.properties` 设置 `FESTAVAL_START_TIME`
- 神兵(`god`/`god_ticket`/`god_exp`)、熔铸(`stonemelt`+`player_dorefine`)入口在春节活动
|
|