MUTEX(互斥體) †
SSP存在確認的方法
SSP啟動時,創建名為"ssp"的MUTEX.下可由此確認SSP是否啟動。
m_hMutex = ::OpenMutex(MUTEX_ALL_ACCESS,FALSE,"ssp");
if(m_hMutex){
//SSP已啟動
}
else{
//SSP未啟動
}
另外,因為Materia等平台對應的MUTEX名為"sakura"。
在SSP的設置裡可以勾選生成名為"sakura"的Mutex來模擬Materia。 (SSP/1.09.00 Pre8追加)
例如:
FMO寫入、讀出時的互斥性可以利用名為"SakuraFMO"的Mutex(SSP/1.08.00追加)加鎖。
可以對讀寫衝突所造成的數據破壞和數據不完全讀出進行有效的阻止,建議在啟動時取得。
代碼用例:
HANDLE hMutex = CreateMutex(NULL,FALSE,"SakuraFMO");
if ( hMutex == NULL ) { 錯誤處理; }
if ( WaitForSingleObject(hMutex, 1000) != WAIT_TIMEOUT ) {
//FMO讀寫;
ReleaseMutex(Mutex);
}
CloseHandle(Mutex);
此外、如果處理中的進程帶著Mutex異常結束、WaitForSingleObject返回WAIT_ABANDONED。
請注意不要忘記和WAIT_OBJECT_0經行比較。
詳細技術細節可以參閱MSDN的Mutex Objects
File Mapping Objects †
當SSP等偽春菜平台運行時會創建一個特定的內存對象。
所有其他進程可以從這個內存對像中以低成本獲取人格的資料信息。
默認該filemapping對象名為"sakura",SSP可通過設置變更FMO名(SSP/1.09.00追加)
內存映射結構
0-3 長度值。指定大小。
4- 數據主體
主體數據格式
主體是連續的一個項目挨著一個項目。
項目形式如下:
[ID].項[1]值[13][10]
例如:
ssp_fmo_header_0000108c_00110554.hwnd [1] 1115476
ssp_fmo_header_0000108c_00110554.name [1] Emily
ssp_fmo_header_0000108c_00110554.keroname [1] Teddy
ssp_fmo_header_0000108c_00110554.path [1] Z:\ssp\
ssp_fmo_header_0000108c_00110554.sakura.surface [1] 5
ssp_fmo_header_0000108c_00110554.kero.surface [1] 10
ssp_fmo_header_0000108c_00110554.kerohwnd [1] 591248
ssp_fmo_header_0000108c_00110554.hwndlist [1] 1115476,591248,787914,787868,656814
ssp_fmo_header_0000108c_00110554.ghostpath [1] Z:\ssp\ghost\emily4\
數據的意義
[ID]在項名稱之前用"."分隔範圍。項名和值是由字符0x01分隔。一行的終止是回車(CR:字符0x0D)+換行(LF:字符0x0A)。
[ID]為SSP的標識,形式為
ssp_fmo_header_[進程ID-16進制]_[Sakura側hwnd-16進制]
樣子的32字節字符串生成,作為毎個人格的唯一標識。可以通過SHIORI Event的uniqueid(即SSP/HF3.0追加的ID,SSP/HF3.2更名為uniqueid)獲得。
注:Materia以進程ID的MD5值作為[ID],但是由於SSP的1個進程建立了複數個人格,所以未使用
有效項如下:
[ID].hwnd | 人格主窗口句柄 |
[ID].name | 活動的人格名稱 |
[ID].keroname | 活動的kero側人格名稱 |
[ID].path | SSP主程序的路徑 |
[ID].sakura.surface | 主人格surface編號(SSP/2.01.47後復活) |
[ID].kero.surface | kero側人格surface編號(SSP/2.01.47後復活) |
[ID].kerohwnd | kero側的hwnd存放unsigned int值。 |
[ID].hwndlist | 逗號分隔至\0的hwnd(無符號數值)列舉,無效窗口為0。 (SSP/2.02.33追加) |
[ID].ghostpath | 人格所在文件的路徑(SSP/1.04.07追加) |
數據大小
暫時的數據大小為1024 * 64個字節。數據的大小也有一個長度值。因此,真正的大小為(1024 * 64) - 4個字節。
yaya可以使用READFMO函數讀取FMO,詳細可見yaya的說明。READFMO
詳細技術細節可以參閱MSDN的File Mapping
SAKURA API †
外部程序可以通過消息隊列使用SAKURA API來進行和偽春菜的互動,如:
UINT WM_SAKURAAPI=RegisterWindowMessage("Sakura");
WPARAM SA_GETPROCESSID=138;
//獲取偽春菜的進程ID。
DWORD processid=(DWORD)SendMessage(TargetHWnd,WM_SAKURAAPI,SA_GETPROCESSID,0);
SAKURA API的參數和作用詳見:開發文件/技術文檔/SAKURA API
Windows消息隊列的詳細技術細節可以參閱MSDN的Messages and Message Queues
Window Class(窗口類) †
主菜窗口:Tmainform
使魔窗口:Tkeroform
其他窗口:Tcharbodywnd
balloon窗口:Tmessageform
使用方式:
//查找偽春菜窗口句柄
HWND TargetHWnd=FindWindow(TEXT("Tmainform"), NULL);
詳細技術細節可以參閱MSDN的Window Classes
Direct SSTP †
Direct SSTP是一種利用窗口消息(非socket)進行SSTP協議通信的辦法。 Direct SSTP具有以下優點:
可以使用SSTP進行簡單快速地通信(對於成本)。
在本地計算機上兩個或更多的服務器很容易共存。 (人格彼此之間的通信很容易。)
Direct SSTP只是一個通信方法,與協議本身無關。
注意: Direct SSTP的執行依賴於微軟Windows。
Direct SSTP使用wm_copydata傳輸數據請求到服務器使用以下協議。 Direct SSTP服務器處理請求並返回相同格式的應答給報頭中指定的HWND窗口句柄。
copydatastruct
dwData = 9801
cbData = 請求報頭的長度
lpData = 請求報頭字符串的指針
請求報頭如
SEND SSTP/1.4
Sender: カードキャプター
Script: \h\s0あーあーあー\e
HWnd: 1024
Charset: Shift_JIS
使用方式為
HGLOBAL hMem=請求報頭指針;
COPYDATASTRUCT copydatastruct;
copydatastruct.dwData=9801;
copydatastruct.cbData=len;
copydatastruct.lpData=hMem;
SendMessage(TargetHWnd,WM_COPYDATA,0,(LPARAM)©datastruct);
請求報頭的格式是與SSTP相同的。然而,應注意的是不管什麼樣的請求HWnd報頭必須包含。不包含HWnd的Direct SSTP通信請求可能會被認為是一個“錯誤的請求”(待考證)。
詳細技術細節可以參閱MSDN的Data Copy
SSTP †
SSTP協議基本結構
概念
所有的偽春菜都能作為SSTP的服務器。服務器通過使用SSTP協議和客戶端通信,並按要求進行各種操作。
通過這種架構,我們可以使偽春菜更具表現性,即給予“偽AI”適當的信息來挖掘出更多的潛力。
由於這項服務是不依賴於操作系統的,所以可能並不止於本地計算機。
例如:
從SSTP客戶端的偽春菜通過互聯網發送SSTP數據包給獨立的計算機。或從Web服務器發送SSTP數據包到的偽春菜客戶機。
操作
偽春菜會偵聽9801端口(和/或端口9821,SSP可設置)。客戶端連接到這個端口,並使用SSTP協議發送數據包。當客戶端發送數據包后,偽春菜在兩秒鐘內獲得一個返回狀態碼。如果返回包正確,偽春菜將回應包中的請求並執行相應的操作。
一台服務器只可以連接到一個客戶端。如果服務器已經連接到另一個客戶端,服務器會返回一個衝突。
通信假定會在很短的時間內完成。因此,如果在短時間內不能結束通訊,服務器將返回鏈接超時並強制斷開連接。
SSTP的協議定義
例如:
SEND SSTP/1.1[CRLF]
Sender: カードキャプター[CRLF]
Script: \0\s0汝のあるべき姿に戻れ。 \e[CRLF]
Option: nodescript,notranslate[CRLF]
Charset: UTF-8[CRLF]
[CRLF]
其中[CRLF]為換行代碼,是一個回車(CR:字符0x0D)+換行(LF:字符0x0A)。發送最後是一個空行。
請求報文
詳見:開發文件/技術文檔/SSTP
返回狀態碼
2xx - 過程完成 |
---|
200 OK | 正常結束 |
204 No Content | 正常結束,但沒有數據被返回 |
210 Break | SSTP發生中斷 |
4xx - 請求錯誤 |
---|
400 Bad Request | 無效的請求內容 |
408 Request Timeout | 數據超時 |
409 Conflict | 另一個客戶端已經連接,或關鍵會話正在運行 |
420 Refuse | 人格拒絕SSTP |
5xx - 服務器錯誤 |
---|
501 Not Implemented | 服務器不支持所請求的功能。 |
503 Service Unavailable | 服務器設置不接受已發送請求。 |
510 Not Local IP | 服務器設置為只接受本地IP請求。 |
511 In Black List | 請求的服務器在服務器黑名單中。 |
512 Invisible | 服務器在無法顯示信息的狀態。 (即使發送請求也是沒有用的。) |
Reguration
通信時限為服務器端計時2秒。如果請求無法在2秒鐘下完成,服務器將返回“請求超時”,並強制斷開連接。
請求報頭的最大長度為2KB=2048byte(本地計算機為16KB=16384byte)的限制。超出長度的報頭會直接斷開并返回Bad Request。
例子
下面的例子只是演示,實用上還需改進
- Perl
sub sendsstp {
use Socket;
$script=$_[0];
$sender='カードキャプター';
$port=9801;
$addr=$ENV{'HTTP_X_FORWARDED_FOR'};
if (index($addr,'.')==-1) { $addr = $ENV{'REMOTE_ADDR'}; }
$proto = getprotobyname('tcp');
socket(S, PF_INET, SOCK_STREAM, $proto);
$ent = sockaddr_in($port, inet_aton($addr));
connect(S, $ent) || die;
select(S); $| = 1; select(STDOUT);
print S "SEND SSTP/1.1\r\n";
print S "Sender: $sender\r\n";
print S "Script: $script\\e\r\n";
print S "\r\n";
$result = <S>;
while (<S>) { print; }
close(S);
return($result);
}
$s=sendsstp('\0\s0‥‥\e');
返回值不是必須的。因此,如果服務器不能使用CGI/SSI,請從其他服務器發送請求。使用如下:
<img src="http://www.koutetsutenshi.net/sstp.cgi" width=1 height=1>
外部連結 †