﻿= 太阳神三国杀通信协议说明文档

Moligaloo <moligaloo@gmail.com>

== 概述
太阳神三国杀使用的是基于 *纯ASCII文本* 的应用层网络协议，基本的通信单位有3类：

. 命令-参数对
. 属性-值对
. 对象名-属性-值的三元组

各通信单位之间以 UNIX 换行符 '\n' 为分隔单位。
客户端发送给服务器的通信单位只有 *命令-参数对* 一种，而服务器给客户端则3种都能包括。

* 命令-参数对
-----------------------
command arg
-----------------------

其中 command 为 Camel 方式命令的命令名，即组成命名名的单词与单词之间无分隔符，
除第一个单词外，其余单词均大写首字母，如 checkVersion，arg 的格式以具体的命令相关。
有些命令无须参数，arg 的值为一个单独的 '.'，用以占位。

* 属性-值对
-----------------------
.property value
-----------------------

用于设置客户端自身玩家对象的一些属性


* 对象名-属性-值的三元组
-----------------------
#objectName property value
-----------------------

用于设置客户端任意玩家（包括自身玩家）的属性。

== 游戏开始前的通信

服务器端以 TCP 方式侦听，默认端口号为 9527。当任何一个新的客户端连接建立后，
服务器主动向客户端发送 2 条命令：checkVersion 和 setup:

=== 版本检查

.checkVersion
-----------------------
checkVersion <version_number>
-----------------------

<version_number> 为版本字符串，一般以该版本的发布日期，例如端午版：

-----------------------
checkVersion 20110606
-----------------------

客户端如果检查到版本与服务的版本不符合，应立即断开连接。

=== 服务器基本设置

.setup
-----------------------
setup <setup_string>
-----------------------

setup_string 由5个部分组成，各个组成部分用冒号分隔:
-----------------
setup_string ::= <server_name>:<game_mode>:<timeout>:<ban_packages>:<flags>
-----------------
如：
-----------------
setup TW9saWdhbG9v55qE5pyN5Yqh5Zmo:08p:0:test+god+joy:FSA
-----------------

* server_name: 
	服务器的名称，由于协议只支持纯文本的通信，为了能使服务器支持中文名等
	字符，需要先将名称以 UTF-8 编码，然后再用 Base64 编码。

* game_mode:
	用以说明服务器的游戏模式。例如 08p 表示普通的八人局，08pd 表示八人局双内。

* timeout:
	游戏的出牌时间，以秒为单位，0 表示不限时间。

* ban_packages:
	在游戏中被禁用的卡牌包，包名之间用+隔开，例如禁用风包与火包，则ban_packages 
	为 wind+fire

* flags:
	具体的标记，目前可用的标记有4个，标记与标记之间 *没有* 分隔，也 *没有* 先后顺序：
	例如 FS 和 SF 是表示相同的含义

-----------------------------
	F: 表示可自由选将	(F= Free choose)
	S: 是否启用双将		(S= Secondard general)
	A: 是否启用AI		(A= AI)
	M: 是否禁用聊天		(M= Mute)
-----------------------------

当客户端核对版本正确且成功解析完服务器提供的设置字符串后，开始发送给服务器第一条命令: signup

=== 登录
signup 有2种情况：

==== 无须密码的格式：
-----------------------------
signup <name>:<avatar>
-----------------------------

* name:
	用户提供的显示名，与服务器提供的服务器名一样，都是先 UTF-8 编码再转成 Base64 编码。
	用户名在无须密码的情况下是 *可以重复* 的。

* avatar:
	用户的头像，一般用武将名，例如 zhangliao 就显示张辽的头像。

==== 需要密码的格式
-----------------------------
signup <name>:<avatar>:<password>
-----------------------------
其中 name 和 avatar 与无须密码时相同，只不过此时 name 不能与其他人重复，作为该用户的独有密码。

* password:
	密码为密码明文通过 MD5 散列而成，用以保证密码的安全。

==== 登录警告
当客户端有2种情况不能成功登录服务器，服务器将以 warn 命令通知客户端，并切断与客户端的连接：

--------------
warn <error_type>
--------------

* error_type 
	目前有3个：
	** INVALID_FORMAT 登录字符串不符合规范
	** REQUIRE_PASSWORD 告知客户端需要密码才能登录，而客户端没有提供密码
	** WRONG_PASSWORD 则表示客户端提供了密码，但是密码不正确。当服务器不需要密码而客户端提供了密码时，此时服务器忽略客户端的密码，不会出现警告。

=== 告知自身玩家对象名

当客户端成功登录以后，服务器将使用第二种通信单位，即属性-值对的格式告知客户端自身玩家的对象名，如
-------------------
.objectName sgs1
-------------------
对象名通常格式为 sgs + 一个数字，如 sgs1, sgs2 等。是用于表示在同一个服务器的玩家内部玩家名，此对象名
全服务器唯一，后面的数字逐次递增。

=== 玩家的加入与退出
每加入一个新的玩家，服务器就使用 addPlayer 命令告知客户端有新的玩家加入，用 removePlayer 来告知玩家的退出。

.addPlayer
-----------------
addPlayer <objectName>:<screen_name>:<avatar>
-----------------

* objectName:
	新的玩家的对象名

* screen_name:
	新玩家的屏幕显示名，也就是玩家登录时采用的名字

* avatar:
	玩家所使用的头像名

.removePlayer
---------------
removePlayer <objectName>
---------------

* objectName:
	退出玩家的对象名

=== 分配身份
当一个房间的人满了以后，游戏会自动开始分配身份。

--------------
.role <lord|loyalist|rebel|renegade>
--------------

* role:
	role 的值为 lord (主公)，loyalist(忠臣), rebel(反贼), renegade(内奸) 4个值之一。
	
如果客户端不是主公，则会额外告知主公玩家，例如主公玩家为 sgs7:

------------------
#sgs7 role lord
------------------

=== 调整座位
当身份分配好了以后，开始用 arrangeSeats 重新调整座位，其中主公固定为1号位。

.arrangeSeats
----------------
arrangeSeats <objectname_list>
----------------

* objectname_list:
	为所有玩家对象名的列表，用+号连接：

------------------------
arrangeSeats sgs7+sgs2+sgs3+sgs4+sgs5+sgs6+sgs1+sgs8
------------------------

=== 倒计时
在游戏分配好座位后，会经过 5 秒的倒计时

.startInXs
------------
startInXs <seconds>
------------

* seconds:
	倒计时的秒数，从5一直递减到0

=== 主公选将
主公开始选将，此命令仅发送给主公玩家，其他玩家只能等待。

.doChooseGeneral
------------------------
doChooseGeneral <lord_list>
------------------------

* lord_list:
	主公玩家武将列表，用+连接，通常是有所有启用包里拥有主公技的武将再加2名随机武将，如
	
-----------------
doChooseGeneral caocao+liubei+sunquan+zhangliao+diaochan
-----------------

客户端点选武将头像后，将用 choose 命令提交结果，例如主公玩家 sgs7 选择了曹操作为主公：

.choose
-----------------
choose caocao
-----------------

主公选择武将以后，将立即将其选择的结果发送给所有的客户端，用对象名-属性-值的三元组，如

-------------
#sgs7 general caocao
-------------

=== 其他玩家选将
选将命令与主公玩家无二，唯一不同的是其他玩家选完了以后，不会立即告知其他人，而是等所有人
全部选完了以后，服务器再告知所有人的选将。

.Note
-------------------
注意，在双将模式中，可能还有第二轮选将，而在一些剧情模式中，
武将是系统自动分配的，不需要选择手工选择
-------------------

=== 设置初始体力值与体力上限
服务器以对象名-属性-值的三元组格式来设置所有人的体力值和体力上限，如刚才的主公：

-------------------
#sgs7 maxhp 5
#sgs7 hp 5
-------------------
	
=== 正式开始
服务器端用 startGame 命令来告诉客户端正式开始工作：

---------------
startGame .
---------------

startGame 命令没有任何参数，不过为了符合格式要求，arg 用 '.' 来占位。

== 游戏开始后的通信
游戏开始后的服务器发送给客户端的命令分为2大类：

. 询问式命令
服务器询问客户端作出某种选择，例如出牌的询问，是否发动技能的询问等，
此类命令都是阻塞式的，必须等到玩家作出选择后才能继续游戏。
如果玩家超时了，通常会强迫其作出一个默认的选择。其实选将也属于这类命令。
询问式命令除了 activate(询问出牌) 以外，剩下的全部以 askFor 和 do 打头。
所以很好辨识。

. 告知式命令
服务器告知服务器有某事发生，例如摸牌，卡牌的移动，战斗记录等。此类命令仅仅是
告知客户端有相关的事情发生，并不需要等待玩家明确的回复。

=== 询问式命令

. activate 出牌阶段请求出牌
. doChooseGeneral 选择武将
. doChooseGeneral2 选择双将中的副将
. doGuanxing 观星
. doGongxin 攻心
. askForDiscard 请求弃牌
. askForExchange 请求换牌
. askForSuit 询问花色选择
. askForKingdom 询问势力选择
. askForSinglePeach 询问出桃
. askForCardChosen 选择卡牌
. askForCard 请求打出某种类型的卡牌
. askForUseCard 请求使用某种类型的卡牌
. askForSkillInvoke 询问是否发动技能
. askForChoice 询问技能的选项
. askForNullification 请求无懈可击
. askForCardShow 请求展示手牌
. askForPindian 请求拼点
. askForYiji 请求遗计
. askForPlayerChosen 请求选择角色
. askForGeneral 请求选择武将

. ....

=== 告知式命令

. checkVersion 版本检查
. setup 告知服务器端配置
. addPlayer 房间加入玩家
. removePlayer 房间退出玩家
. startInXs 倒计时
. arrangeSeats 调整座位
. warn 登录警告

. startGame 开始游戏
. gameOver 游戏结束

. hpChange 角色体力值变化
. killPlayer 角色阵亡
. revivePlayer 角色复活
. showCard 角色展示卡牌
. setMark 角色设置标记
. log 战斗日志
. speak 玩家聊天
. addHistory 加入出牌历史
. acqureSkill 获得技能
. attachSkill 附着技能
. detachSkill 失去技能
. moveFocus 移动焦点
. setEmotion 设置角色表情
. skillInvoked 告知技能的触发
. animate 动画
. jilei 鸡肋指令（杨修专用）
. judgeResult 告知判定的结果
. setScreenName 设置所有玩家屏显名（仅在竞赛模式中游戏结束时触发）
. setFixedDistance
. pile 通知私有牌堆更新
. transfigure 变身

. playSkillEffect 播放技能配音
. playCardEffect 播放卡牌配音
. playAudio 播放其他音频

. clearPile 弃牌堆清空
. setPileNumber 设置摸牌堆数量
. moveNCards 卡牌移动（隐藏）
. moveCard 卡牌移动（公开）
. drawNCards 摸牌命令(其他人摸牌，他人)
. drawCards 摸牌命令（自己摸牌）

==== activate
----------------
activate <objectName>
----------------

* objectName:
	请求出牌的玩家对象名，若此对象名为自身对象名，则开始出牌或使用技能。

对应的回复命令为

------------------
useCard <card_string->target_list>
------------------

* card_string:
	用于描述卡牌的字符串，见卡牌字符串的详述

* target_list:
	目标列表，是一个卡牌的使用对象的对象名列表，与大多数列表的
	内联表示一样，用+号连接。

例子：
--------------
useCard 0->sgs7+sgs3
---------------

如果不想使用任何卡牌，直接用 "useCard ." ，
此时服务器认为客户端出牌结束

==== askForSkillInvoke

询问是否发动技能

--------------------
askForSkillInvoke <skill_name>
--------------------

* skill_name:
	技能名字，如 jianxiong 之类

对应的回复命令：
--------------------
invokeSkill <yes|no>
--------------------

若发动，则回复 "invokeSkill yes" 反之 "invokeSkill no"

==== askForChoice

askForChoice 用于询问选择技能的选项，例如董卓的崩坏，
有流失体力和减少体力上限2个选项

------------------
askForChoice <skill_name>:<choice_list>
------------------

* skill_name:
	技能的名称

* choice_list:
	选项，同理用+连接

例子:
-----------------
askForChoice benghuai:maxhp+hp
-----------------
这个就是询问董卓的崩坏是选择减少体力上限还是流失1点体力

==== askForCard

askForCard 用于询问是否“打出”某张牌，例如南蛮要打出杀，格式为：

-------------
askForCard <pattern>:<prompt>
-------------

* pattern:
	卡牌的模式，目前主要就2种情况：
	. slash(杀): 南蛮、决斗、激将
	. jink(闪): 杀、万箭齐发、护驾

* prompt:
	说明具体的出牌原因

	出杀
	. 南蛮出杀：	@savage-assault-slash:<from>, <from> 为南蛮入侵的使用者
	. 决斗出杀：	@duel-slash:<from>, <from> 为决斗的敌对方(不一定是决斗的发起者)
	. 激将出杀：	@jijiang-slash:<from>, <from> 为激将的发起者（一般是刘备）

	出闪
	. 杀出闪：	@slash-jink:<from>, <from> 为使用杀的玩家
	. 万箭齐发出闪：	@archery-attack-jink:<from>, <from> 为万箭齐发的使用者
	. 护驾出闪:	@hujia-jink:<from>, <from> 为护驾的发起者（一般是曹操）

	针对吕布无双的情况, <from> 均为吕布
	. 无双杀出第一张闪 @wushuang-jink-1:<from>
	. 无双杀出第二张闪 @wushuang-jink-2:<from>
	. 无双决斗出第一张杀 @wushuang-slash-1:<from>
	. 无双决斗出第二张杀 @wushuang-slash-2:<from>


== 游戏的结束

当游戏满足结束条件时，服务器以 gameOver 命令来结束游戏。

-------------
gameOver <winner_list>
-------------

winner_list:
	胜利者列表，有2种变体：

	. 玩家身份，例如 gameOver lord+loyalist 表示主公和忠臣取得胜利
	. 玩家对象名，例如 gameOver sgs7+sgs5 表示 sgs7 和 sgs5 一起胜利

.Note
--------------------
平局时，winner_list 为 '.'，此时无人胜利 -_-!
--------------------

== 卡牌字符串 





