forked from Hay1tsme/segatools
ekt: implement y3ws
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -18,6 +18,7 @@ build/
|
|||||||
|
|
||||||
# External dependencies
|
# External dependencies
|
||||||
subprojects/capnhook
|
subprojects/capnhook
|
||||||
|
subprojects/cwinwebsocket
|
||||||
|
|
||||||
# For enabling debug logging on local builds
|
# For enabling debug logging on local builds
|
||||||
MesonLocalOptions.mk
|
MesonLocalOptions.mk
|
||||||
|
39
common/y3io/impl/websockets/config.c
Normal file
39
common/y3io/impl/websockets/config.c
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
void y3ws_config_load(struct y3ws_config *cfg, const wchar_t *filename)
|
||||||
|
{
|
||||||
|
assert(cfg != NULL);
|
||||||
|
assert(filename != NULL);
|
||||||
|
|
||||||
|
cfg->enable = GetPrivateProfileIntW(L"y3ws", L"enable", 1, filename);
|
||||||
|
cfg->debug = GetPrivateProfileIntW(L"y3ws", L"debug", 0, filename);
|
||||||
|
|
||||||
|
cfg->port = GetPrivateProfileIntW(L"y3ws", L"port", 3497, filename);
|
||||||
|
|
||||||
|
wchar_t tmpstr[16];
|
||||||
|
|
||||||
|
GetPrivateProfileStringW(
|
||||||
|
L"y3ws",
|
||||||
|
L"gameId",
|
||||||
|
L"SDEY",
|
||||||
|
tmpstr,
|
||||||
|
_countof(tmpstr),
|
||||||
|
filename);
|
||||||
|
|
||||||
|
wcstombs(cfg->game_id, tmpstr, sizeof(cfg->game_id));
|
||||||
|
|
||||||
|
GetPrivateProfileStringW(
|
||||||
|
L"y3ws",
|
||||||
|
L"cardDirectory",
|
||||||
|
L"DEVICE\\print",
|
||||||
|
cfg->card_path,
|
||||||
|
_countof(cfg->card_path),
|
||||||
|
filename);
|
||||||
|
}
|
19
common/y3io/impl/websockets/config.h
Normal file
19
common/y3io/impl/websockets/config.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include "hooklib/dvd.h"
|
||||||
|
#include "hooklib/touch.h"
|
||||||
|
#include "hooklib/printer_chc.h"
|
||||||
|
#include "hooklib/printer_cx.h"
|
||||||
|
|
||||||
|
struct y3ws_config {
|
||||||
|
bool enable;
|
||||||
|
bool debug;
|
||||||
|
|
||||||
|
uint16_t port;
|
||||||
|
char game_id[5];
|
||||||
|
wchar_t card_path[MAX_PATH];
|
||||||
|
};
|
||||||
|
|
||||||
|
void y3ws_config_load(struct y3ws_config *cfg, const wchar_t *filename);
|
351
common/y3io/impl/websockets/y3ws.c
Normal file
351
common/y3io/impl/websockets/y3ws.c
Normal file
@ -0,0 +1,351 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
|
||||||
|
#include "3rdparty/cjson/cJSON.h"
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "y3ws.h"
|
||||||
|
|
||||||
|
#include <wchar.h>
|
||||||
|
|
||||||
|
#include "hooklib/y3.h"
|
||||||
|
|
||||||
|
#include "winwebsocket.h"
|
||||||
|
#include "lib/base64.h"
|
||||||
|
|
||||||
|
#include "util/dprintf.h"
|
||||||
|
#include "util/env.h"
|
||||||
|
|
||||||
|
static void onopen(struct wws_connection*);
|
||||||
|
static void onclose(struct wws_connection*);
|
||||||
|
static void onmessage(struct wws_connection*, const char*, size_t);
|
||||||
|
static void onlog(const char*, ...);
|
||||||
|
|
||||||
|
#define PROTOCOL_VERSION 1
|
||||||
|
#define GAME_MAX_CARDS 32
|
||||||
|
#define MAX_CARDS 500
|
||||||
|
#define CARD_ID_LEN 5
|
||||||
|
#define OUTPUT_BUFFER_SIZE (1024 * 1024 * 5)
|
||||||
|
|
||||||
|
static struct y3ws_config cfg;
|
||||||
|
|
||||||
|
static bool is_initialized = false;
|
||||||
|
static struct CardInfo card_info[GAME_MAX_CARDS];
|
||||||
|
static int card_info_size = 0;
|
||||||
|
static CRITICAL_SECTION card_info_lock;
|
||||||
|
|
||||||
|
#pragma region y3-dll functions
|
||||||
|
uint16_t y3_io_get_api_version() {
|
||||||
|
return 0x0100;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT y3_io_init() {
|
||||||
|
if (is_initialized) {
|
||||||
|
return S_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
y3ws_config_load(&cfg, get_config_path());
|
||||||
|
|
||||||
|
memset(card_info, 0, sizeof(card_info));
|
||||||
|
InitializeCriticalSection(&card_info_lock);
|
||||||
|
|
||||||
|
wws_set_callbacks(onopen, onclose, onmessage, onlog);
|
||||||
|
wws_set_verbose(cfg.debug);
|
||||||
|
|
||||||
|
HRESULT hr = wws_start(cfg.port);
|
||||||
|
|
||||||
|
if (SUCCEEDED(hr)) {
|
||||||
|
dprintf("Y3WS: Started server on port %d\n", cfg.port);
|
||||||
|
if (cfg.debug) {
|
||||||
|
dprintf("Y3WS: WS debug logging is on\n");
|
||||||
|
}
|
||||||
|
is_initialized = true;
|
||||||
|
} else {
|
||||||
|
dprintf("Y3WS: Error starting server: %lx\n", hr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT y3_io_close() {
|
||||||
|
|
||||||
|
HRESULT hr = wws_stop();
|
||||||
|
|
||||||
|
DeleteCriticalSection(&card_info_lock);
|
||||||
|
|
||||||
|
is_initialized = false;
|
||||||
|
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT y3_io_get_cards(struct CardInfo* pCardInfo, int* numCards) {
|
||||||
|
EnterCriticalSection(&card_info_lock);
|
||||||
|
for (int i = 0; i < card_info_size; i++) {
|
||||||
|
memcpy(&pCardInfo[i], &card_info[i], sizeof(struct CardInfo));
|
||||||
|
}
|
||||||
|
*numCards = card_info_size;
|
||||||
|
LeaveCriticalSection(&card_info_lock);
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
#pragma region y3ws functions
|
||||||
|
static void y3ws_make_error_packet(const char* message, char* output_data, size_t* output_size) {
|
||||||
|
dprintf("Y3WS: Error: %s\n", message);
|
||||||
|
*output_size = sprintf_s((char*)output_data, *output_size, "{\"version\":%d, \"success\":false,\"error\":\"%s\"}", PROTOCOL_VERSION, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void y3ws_make_success_packet(char* output_data, size_t* output_size) {
|
||||||
|
*output_size = sprintf_s((char*)output_data, *output_size, "{\"version\":%d, \"success\":true}", PROTOCOL_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void y3ws_read_cards(char* output_data, size_t* output_size) {
|
||||||
|
WIN32_FIND_DATAW ffd;
|
||||||
|
|
||||||
|
wchar_t path[MAX_PATH];
|
||||||
|
swprintf(path, MAX_PATH, L"%ls\\*", cfg.card_path);
|
||||||
|
|
||||||
|
HANDLE hFind = FindFirstFileW(path, &ffd);
|
||||||
|
if (INVALID_HANDLE_VALUE == hFind) {
|
||||||
|
dprintf("Y3WS: Failed to access directory: %ls (%ld)\n", path, GetLastError());
|
||||||
|
y3ws_make_error_packet("Could not read from card directory", output_data, output_size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON* response = cJSON_CreateObject();
|
||||||
|
cJSON* pversion = cJSON_CreateNumber(PROTOCOL_VERSION);
|
||||||
|
cJSON_AddItemToObject(response, "version", pversion);
|
||||||
|
cJSON* success = cJSON_CreateBool(true);
|
||||||
|
cJSON_AddItemToObject(response, "success", success);
|
||||||
|
cJSON* cards = cJSON_CreateArray();
|
||||||
|
cJSON_AddItemToObject(response, "cards", cards);
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
do {
|
||||||
|
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
swprintf(path, MAX_PATH, L"%ls\\%ls", cfg.card_path, ffd.cFileName);
|
||||||
|
|
||||||
|
HANDLE hImage = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
|
if (hImage == INVALID_HANDLE_VALUE) {
|
||||||
|
dprintf("Y3WS: Error opening %ls: %ld\n", path, GetLastError());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
DWORD ret = SetFilePointer(hImage, -CARD_ID_LEN, NULL, FILE_END);
|
||||||
|
if (ret == INVALID_SET_FILE_POINTER) {
|
||||||
|
dprintf("Y3WS: Error seeking in %ls: %ld\n", path, GetLastError());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
uint8_t buf[CARD_ID_LEN];
|
||||||
|
DWORD bytesRead = 0;
|
||||||
|
if (!ReadFile(hImage, &buf, CARD_ID_LEN, &bytesRead, NULL) || bytesRead != CARD_ID_LEN) {
|
||||||
|
dprintf("Y3WS: Error reading card ID from %ls: %ld\n", path, GetLastError());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON* card = cJSON_CreateObject();
|
||||||
|
cJSON_AddItemToArray(cards, card);
|
||||||
|
|
||||||
|
// cJSON isn't wide...
|
||||||
|
char fpatha[MAX_PATH];
|
||||||
|
size_t fpatha_len = 0;
|
||||||
|
wcstombs_s(&fpatha_len, fpatha, MAX_PATH, ffd.cFileName, MAX_PATH);
|
||||||
|
fpatha[fpatha_len] = 0;
|
||||||
|
|
||||||
|
// ReSharper disable once CppRedundantCastExpression
|
||||||
|
cJSON* path_str = cJSON_CreateString(fpatha);
|
||||||
|
cJSON_AddItemToObject(card, "path", path_str);
|
||||||
|
cJSON* card_id = cJSON_CreateNumber((double)((uint64_t)buf[0] >> 32 | buf[1] >> 24 | buf[2] >> 16 | buf[3] >> 8 | buf[4]));
|
||||||
|
cJSON_AddItemToObject(card, "card_id", card_id);
|
||||||
|
|
||||||
|
} while (FindNextFileW(hFind, &ffd) != 0 && count++ < MAX_CARDS);
|
||||||
|
|
||||||
|
cJSON_PrintPreallocated(response, output_data, (int)*output_size, false);
|
||||||
|
*output_size = strlen(output_data);
|
||||||
|
|
||||||
|
dprintf("Y3WS: Sent %d card(s) to the client\n", count);
|
||||||
|
|
||||||
|
cJSON_Delete(response);
|
||||||
|
FindClose(hFind);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void y3ws_read_card_image(char* file_name, char* output_data, size_t* output_size) {
|
||||||
|
wchar_t path[MAX_PATH];
|
||||||
|
swprintf(path, MAX_PATH, L"%ls\\%s", cfg.card_path, file_name);
|
||||||
|
|
||||||
|
HANDLE hImage = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
|
if (hImage == INVALID_HANDLE_VALUE) {
|
||||||
|
dprintf("Y3WS: Failed to access file: %ls (%ld)\n", path, GetLastError());
|
||||||
|
y3ws_make_error_packet("Could not read file", output_data, output_size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD size = GetFileSize(hImage, NULL);
|
||||||
|
if (size == INVALID_FILE_SIZE) {
|
||||||
|
dprintf("Y3WS: Failed to access file size: %ls (%ld)\n", path, GetLastError());
|
||||||
|
y3ws_make_error_packet("Could not read file", output_data, output_size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t* buf = malloc(size);
|
||||||
|
size_t b64size = ((size * 4) / 3) + (size / 96) + 6;
|
||||||
|
char* b64buf = malloc(b64size);
|
||||||
|
if (buf == NULL) {
|
||||||
|
dprintf("Y3WS: Allocation error for file: %ls (%ld)\n", path, GetLastError());
|
||||||
|
y3ws_make_error_packet("Could not read file", output_data, output_size);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD bytesRead = 0;
|
||||||
|
if (!ReadFile(hImage, buf, size, &bytesRead, NULL) || bytesRead != size) {
|
||||||
|
dprintf("Y3WS: File read failed: %ls (%ld)\n", path, GetLastError());
|
||||||
|
y3ws_make_error_packet("Could not read file", output_data, output_size);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
b64size = base64_encode(buf, size, b64buf);
|
||||||
|
|
||||||
|
if (b64size + 100 > *output_size) {
|
||||||
|
dprintf("Y3WS: Encoded file size exceeds buffer: %ls (%lld / %lld)\n", path, b64size, *output_size);
|
||||||
|
y3ws_make_error_packet("File too large", output_data, output_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
*output_size = sprintf_s((char*)output_data, *output_size, "{\"version\":%d, \"success\":true,\"data\":\"%s\"}", PROTOCOL_VERSION, (char*)b64buf);
|
||||||
|
end:
|
||||||
|
free(buf);
|
||||||
|
free(b64buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void y3ws_set_cards_from_json(const cJSON* cards, char* output_data, size_t* output_size) {
|
||||||
|
|
||||||
|
const cJSON* card = NULL;
|
||||||
|
card_info_size = 0;
|
||||||
|
|
||||||
|
EnterCriticalSection(&card_info_lock);
|
||||||
|
memset(card_info, 0, sizeof(card_info));
|
||||||
|
cJSON_ArrayForEach(card, cards) {
|
||||||
|
cJSON* x = cJSON_GetObjectItemCaseSensitive(card, "x");
|
||||||
|
cJSON* y = cJSON_GetObjectItemCaseSensitive(card, "y");
|
||||||
|
cJSON* r = cJSON_GetObjectItemCaseSensitive(card, "rotation");
|
||||||
|
cJSON* id = cJSON_GetObjectItemCaseSensitive(card, "card_id");
|
||||||
|
|
||||||
|
if (cJSON_IsNumber(x) && cJSON_IsNumber(y) && cJSON_IsNumber(r) && cJSON_IsNumber(id)) {
|
||||||
|
card_info[card_info_size].eCardStatus = VALID;
|
||||||
|
card_info[card_info_size].fX = (float)x->valuedouble;
|
||||||
|
card_info[card_info_size].fY = (float)y->valuedouble;
|
||||||
|
card_info[card_info_size].fAngle = (float)r->valuedouble;
|
||||||
|
card_info[card_info_size].uID = id->valueint;
|
||||||
|
} else {
|
||||||
|
dprintf("Y3WS: Invalid object in card array at index %d\n", card_info_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (++card_info_size >= GAME_MAX_CARDS) {
|
||||||
|
dprintf("Y3WS: too many cards specified, truncating!\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
LeaveCriticalSection(&card_info_lock);
|
||||||
|
y3ws_make_success_packet(output_data, output_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void y3ws_handle(const char* input_data, uint32_t input_length, char* output_data, size_t* output_size) {
|
||||||
|
cJSON* json = cJSON_ParseWithLength(input_data, input_length);
|
||||||
|
if (json == NULL) {
|
||||||
|
const char *error_ptr = cJSON_GetErrorPtr();
|
||||||
|
|
||||||
|
dprintf("Y3WS: Invalid JSON received!\n");
|
||||||
|
dprintf("Y3WS: Message was: %s\n", input_data);
|
||||||
|
dprintf("Y3WS: Error at: %s\n", error_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
const cJSON* ver = cJSON_GetObjectItemCaseSensitive(json, "version");
|
||||||
|
const cJSON* cmd = cJSON_GetObjectItemCaseSensitive(json, "command");
|
||||||
|
|
||||||
|
if (!cJSON_IsNumber(ver) || ver->valueint <= 0) {
|
||||||
|
y3ws_make_error_packet("Missing version attribute", output_data, output_size);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
if (!cJSON_IsString(cmd)) {
|
||||||
|
y3ws_make_error_packet("Missing command attribute", output_data, output_size);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
if (ver->valueint != PROTOCOL_VERSION) {
|
||||||
|
y3ws_make_error_packet("Incompatible version", output_data, output_size);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(cmd->valuestring, "ping") == 0) {
|
||||||
|
y3ws_make_success_packet(output_data, output_size);
|
||||||
|
} else if (strcmp(cmd->valuestring, "get_game_id") == 0) {
|
||||||
|
*output_size = sprintf_s((char*)output_data, *output_size, "{\"version\":%d, \"success\":true,\"game_id\":\"%s\"}", PROTOCOL_VERSION, cfg.game_id);
|
||||||
|
} else if (strcmp(cmd->valuestring, "get_cards") == 0) {
|
||||||
|
y3ws_read_cards(output_data, output_size);
|
||||||
|
} else if (strcmp(cmd->valuestring, "get_card_image") == 0) {
|
||||||
|
const cJSON* path = cJSON_GetObjectItemCaseSensitive(json, "path");
|
||||||
|
if (cJSON_IsString(path)) {
|
||||||
|
y3ws_read_card_image(path->valuestring, output_data, output_size);
|
||||||
|
} else {
|
||||||
|
y3ws_make_error_packet("Missing attribute", output_data, output_size);
|
||||||
|
}
|
||||||
|
} else if (strcmp(cmd->valuestring, "set_field") == 0) {
|
||||||
|
const cJSON* cards = cJSON_GetObjectItemCaseSensitive(json, "cards");
|
||||||
|
if (cJSON_IsArray(cards)) {
|
||||||
|
y3ws_set_cards_from_json(cards, output_data, output_size);
|
||||||
|
} else {
|
||||||
|
y3ws_make_error_packet("Missing attribute", output_data, output_size);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
y3ws_make_error_packet("Unknown command", output_data, output_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
cJSON_Delete(json);
|
||||||
|
}
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
#pragma region websocket callbacks
|
||||||
|
|
||||||
|
static void onopen(struct wws_connection* client) {
|
||||||
|
dprintf("Y3WS: Connection opened, addr: %s\n", client->ip_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void onclose(struct wws_connection* client) {
|
||||||
|
dprintf("Y3WS: Connection closed, addr: %s\n", client->ip_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void onmessage(struct wws_connection* client, const char* msg, size_t size) {
|
||||||
|
if (cfg.debug) {
|
||||||
|
dprintf("Y3WS: Message: %.*s\n", (int)size, msg);
|
||||||
|
}
|
||||||
|
char* out_buf = malloc(OUTPUT_BUFFER_SIZE);
|
||||||
|
if (out_buf == NULL) {
|
||||||
|
dprintf("Y3WS: out of memory for allocating response buffer\n");
|
||||||
|
client->is_connected = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
size_t out_size = OUTPUT_BUFFER_SIZE;
|
||||||
|
|
||||||
|
y3ws_handle(msg, size, out_buf, &out_size);
|
||||||
|
HRESULT hr = wws_send(client, out_buf, out_size);
|
||||||
|
if (!SUCCEEDED(hr)) {
|
||||||
|
dprintf("Y3WS: Error sending message: %lx\n", hr);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(out_buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void onlog(const char* format, ...) {
|
||||||
|
va_list args;
|
||||||
|
va_start (args, format);
|
||||||
|
dprintf("Websocket: ");
|
||||||
|
dprintfv(format, args);
|
||||||
|
va_end (args);
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma endregion
|
4
common/y3io/impl/websockets/y3ws.h
Normal file
4
common/y3io/impl/websockets/y3ws.h
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <windows.h>
|
@ -6,12 +6,20 @@ y3io_lib = static_library(
|
|||||||
dependencies : [
|
dependencies : [
|
||||||
capnhook.get_variable('hook_dep'),
|
capnhook.get_variable('hook_dep'),
|
||||||
capnhook.get_variable('hooklib_dep'),
|
capnhook.get_variable('hooklib_dep'),
|
||||||
|
cwinwebsocket.get_variable('cws_dep'),
|
||||||
],
|
],
|
||||||
link_with : [
|
link_with : [
|
||||||
util_lib,
|
util_lib,
|
||||||
],
|
],
|
||||||
sources : [
|
sources : [
|
||||||
'impl/dummy/y3io.c',
|
'impl/websockets/config.c',
|
||||||
|
'impl/websockets/config.h',
|
||||||
|
'impl/websockets/y3ws.c',
|
||||||
|
'impl/websockets/y3ws.h',
|
||||||
|
'impl/websockets/3rdparty/cjson/cJSON.c',
|
||||||
|
'impl/websockets/3rdparty/cjson/cJSON.h',
|
||||||
|
'impl/websockets/3rdparty/cjson/cJSON_Utils.c',
|
||||||
|
'impl/websockets/3rdparty/cjson/cJSON_Utils.h',
|
||||||
'impl/y3io.h',
|
'impl/y3io.h',
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
55
doc/y3ws.txt
Normal file
55
doc/y3ws.txt
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
Y3WS websocket protocol for card I/O for the taisen series by Haruka:
|
||||||
|
|
||||||
|
All packets are JSON and, unless otherwise specified contain these fields:
|
||||||
|
|
||||||
|
* Requests:
|
||||||
|
{"version": 1, "command": "..."}
|
||||||
|
|
||||||
|
* Responses:
|
||||||
|
{"version": 1, "success": <bool>, "error": <null/string>}
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
|
||||||
|
- ping
|
||||||
|
|
||||||
|
Does nothing except answering with a success response.
|
||||||
|
|
||||||
|
|
||||||
|
- get_game_id
|
||||||
|
|
||||||
|
Extra response fields:
|
||||||
|
- game_id: <string>
|
||||||
|
|
||||||
|
Returns the current game ID that is being run (SDDD, SDGY).
|
||||||
|
|
||||||
|
|
||||||
|
- get_cards
|
||||||
|
|
||||||
|
Extra response fields:
|
||||||
|
- array of objects
|
||||||
|
- card_id: <long>
|
||||||
|
- path: <string>
|
||||||
|
|
||||||
|
Returns all cards that the player has in possession.
|
||||||
|
|
||||||
|
|
||||||
|
- get_card_image
|
||||||
|
|
||||||
|
Extra request fields:
|
||||||
|
- path: <string>
|
||||||
|
Extra response fields:
|
||||||
|
- data: <base64 encoded string>
|
||||||
|
|
||||||
|
Returns the card image for the given path (format TBA).
|
||||||
|
|
||||||
|
|
||||||
|
- set_field
|
||||||
|
|
||||||
|
Extra request fields:
|
||||||
|
- array of objects
|
||||||
|
- card_id: <long>
|
||||||
|
- x: <float>
|
||||||
|
- y: <float>
|
||||||
|
- rotation: <float>
|
||||||
|
|
||||||
|
Sets the given cards onto the given positions on the field. Always replaces the entire board.
|
@ -97,6 +97,7 @@ bcrypt_lib = cc.find_library('bcrypt')
|
|||||||
|
|
||||||
inc = include_directories('common', 'games')
|
inc = include_directories('common', 'games')
|
||||||
capnhook = subproject('capnhook')
|
capnhook = subproject('capnhook')
|
||||||
|
cwinwebsocket = subproject('cwinwebsocket')
|
||||||
|
|
||||||
subdir('common/amex')
|
subdir('common/amex')
|
||||||
subdir('common/iccard')
|
subdir('common/iccard')
|
||||||
|
5
subprojects/cwinwebsocket.wrap
Normal file
5
subprojects/cwinwebsocket.wrap
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[wrap-git]
|
||||||
|
directory = cwinwebsocket
|
||||||
|
url = https://github.com/akechi-haruka/cwinwebsocket
|
||||||
|
revision = HEAD
|
||||||
|
|
Reference in New Issue
Block a user