323 lines
8.0 KiB
D
323 lines
8.0 KiB
D
import vibe.vibe;
|
|
|
|
import std.base64 : Base64;
|
|
import std.conv : to;
|
|
|
|
import botan.filters.pipe : Pipe;
|
|
import botan.filters.b64_filt : Base64Encoder, Base64Decoder;
|
|
import botan.filters.transform_filter : TransformationFilter;
|
|
import botan.compression.zlib: ZlibDecompression;
|
|
import botan.libstate.lookup : getCipher;
|
|
import botan.algo_base.symkey : SymmetricKey, InitializationVector;
|
|
import botan.utils.types;
|
|
|
|
import config;
|
|
|
|
void main()
|
|
{
|
|
auto router = new URLRouter;
|
|
router.get("*", reverseProxyRequest(serverHost, serverPort));
|
|
router.post("/api/v1/*", &onAPIRequest);
|
|
|
|
auto settings = new HTTPServerSettings;
|
|
settings.port = proxyPortNumber;
|
|
settings.options = HTTPServerOption.parseURL | HTTPServerOption.errorStackTraces;
|
|
listenHTTP(settings, router);
|
|
|
|
runApplication();
|
|
}
|
|
|
|
void onAPIRequest(HTTPServerRequest request, HTTPServerResponse response)
|
|
{
|
|
auto apiEndpoint = request.path.split("/api/v1")[1];
|
|
auto nonce = request.headers["auth_nonce"];
|
|
|
|
auto requestBody = request.bodyReader.readAll();
|
|
auto requestJson = getRequestJson(requestBody, apiEndpoint, nonce);
|
|
handleRequest(apiEndpoint, requestJson);
|
|
|
|
//TODO: Allow request editing (requires recalculation of signature)
|
|
auto apiResponse = sendRequest(apiEndpoint, request.headers.dup, requestBody);
|
|
|
|
auto responseBody = apiResponse.bodyReader.readAll();
|
|
auto responseJson = getResponseJson(responseBody, apiEndpoint, nonce);
|
|
|
|
auto finalResponseJson = handleResponse(apiEndpoint, responseJson);
|
|
auto finalResponseBody = constructResponseBody(apiEndpoint, finalResponseJson, nonce);
|
|
|
|
sendResponse(response, apiResponse, finalResponseBody);
|
|
|
|
import std.stdio : writeln;
|
|
writeln(apiEndpoint);
|
|
}
|
|
|
|
Json getRequestJson(ubyte[] input, string endpoint, string nonce)
|
|
{
|
|
auto originalRequestPipe = Pipe
|
|
(
|
|
new Base64Decoder,
|
|
getCipher
|
|
(
|
|
cipherName,
|
|
SymmetricKey((cast(ubyte[]) keyString).ptr, keyString.length),
|
|
InitializationVector((cast(ubyte[]) nonce).ptr, nonce.length),
|
|
DECRYPTION
|
|
)
|
|
);
|
|
|
|
originalRequestPipe.processMsg(input.ptr, input.length);
|
|
|
|
auto requestJsonString = originalRequestPipe.toString;
|
|
return requestJsonString.parseJson;
|
|
}
|
|
|
|
Json getResponseJson(ubyte[] input, string endpoint, string nonce)
|
|
{
|
|
import std.zlib : uncompress;
|
|
|
|
switch(endpoint)
|
|
{
|
|
case "/config/getGameConfig":
|
|
return getRequestJson(input, endpoint, nonce);
|
|
case "/master/getMaster":
|
|
auto decompressedInput = cast(ubyte[])uncompress(input); //TODO: Use pipe instead (how?)
|
|
|
|
auto responseJsonString = cast(string) decompressedInput;
|
|
return responseJsonString.parseJson;
|
|
default:
|
|
auto decompressedInput = cast(ubyte[])uncompress(input); //TODO: Use pipe instead (how?)
|
|
|
|
auto responsePipe = Pipe
|
|
(
|
|
new Base64Decoder
|
|
);
|
|
|
|
responsePipe.processMsg(decompressedInput.ptr, decompressedInput.length);
|
|
|
|
auto responseJsonString = responsePipe.toString;
|
|
return responseJsonString.parseJson;
|
|
}
|
|
}
|
|
|
|
void handleRequest(string endpoint, Json json)
|
|
{
|
|
|
|
}
|
|
|
|
Json handleResponse(string endpoint, Json json)
|
|
{
|
|
if(endpoint == "/master/getMaster")
|
|
{
|
|
import models.master;
|
|
auto getMasterResponse = json.deserializeJson!GetMasterResponse;
|
|
|
|
foreach(character; getMasterResponse.masterCharacter)
|
|
{
|
|
auto characterId = character[0].to!uint;
|
|
|
|
import translations.character;
|
|
if(characterId in characterNames)
|
|
{
|
|
character[5] = characterNames[characterId];
|
|
}
|
|
}
|
|
|
|
foreach(characterSkill; getMasterResponse.masterCharacterSkill)
|
|
{
|
|
auto skillId = characterSkill[0].to!uint;
|
|
|
|
import translations.characterSkill;
|
|
if(skillId in skillNames)
|
|
{
|
|
characterSkill[1] = skillNames[skillId];
|
|
characterSkill[6] = skillDescriptions[skillId];
|
|
}
|
|
}
|
|
|
|
foreach(stage; getMasterResponse.masterStage)
|
|
{
|
|
auto stageId = stage[0].to!uint;
|
|
|
|
import translations.stage;
|
|
if(stageId in stageNames)
|
|
{
|
|
stage[1] = stageNames[stageId];
|
|
}
|
|
}
|
|
|
|
foreach(mission; getMasterResponse.masterMission)
|
|
{
|
|
//TODO: Figure out a way to find the wrong ones
|
|
auto missionId = mission[0].to!uint;
|
|
|
|
import translations.mission;
|
|
if(missionId in missionNames)
|
|
{
|
|
mission[1] = missionNames[missionId];
|
|
mission[2] = missionDescriptions[missionId];
|
|
mission[3] = missionRewards[missionId];
|
|
}
|
|
}
|
|
|
|
foreach(dungeon; getMasterResponse.masterDungeon)
|
|
{
|
|
auto dungeonId = dungeon[10].to!uint;
|
|
|
|
import translations.dungeon;
|
|
if(dungeonId in dungeonNames)
|
|
{
|
|
dungeon[12] = dungeonNames[dungeonId];
|
|
}
|
|
}
|
|
|
|
foreach(guest; getMasterResponse.masterGuest)
|
|
{
|
|
auto guestId = guest[0].to!uint;
|
|
|
|
import translations.guest;
|
|
if(guestId in guestPartyNames)
|
|
{
|
|
guest[1] = guestPartyNames[guestId];
|
|
}
|
|
}
|
|
|
|
foreach(item; getMasterResponse.masterItem)
|
|
{
|
|
auto itemId = item[0].to!uint;
|
|
|
|
import translations.item;
|
|
if(itemId in itemNames)
|
|
{
|
|
item[1] = itemNames[itemId];
|
|
}
|
|
}
|
|
|
|
foreach(location; getMasterResponse.masterWorldMap)
|
|
{
|
|
auto locationId = location[0].to!uint;
|
|
|
|
import translations.worldMap;
|
|
if(locationId in worldMapLocationNames)
|
|
{
|
|
location[3] = worldMapLocationNames[locationId];
|
|
location[4] = worldMapLocationCaptions[locationId];
|
|
location[5] = worldMapLocationDescriptions[locationId];
|
|
}
|
|
}
|
|
|
|
foreach(resource; getMasterResponse.masterCharacterTextResource)
|
|
{
|
|
auto resourceId = resource[0].to!uint;
|
|
|
|
import translations.characterTextResource;
|
|
if(resourceId in characterTextResource)
|
|
{
|
|
resource[3] = characterTextResource[resourceId];
|
|
}
|
|
}
|
|
|
|
foreach(leaderSkill; getMasterResponse.masterCharacterLeaderSkill)
|
|
{
|
|
auto leaderSkillId = leaderSkill[0].to!uint;
|
|
|
|
import translations.characterLeaderSkill;
|
|
if(leaderSkillId in characterLeaderSkillNames)
|
|
{
|
|
leaderSkill[1] = characterLeaderSkillNames[leaderSkillId];
|
|
leaderSkill[14] = characterLeaderSkillDescriptions[leaderSkillId];
|
|
}
|
|
}
|
|
|
|
foreach(characterBookEntry; getMasterResponse.masterCharacterBook)
|
|
{
|
|
auto characterId = characterBookEntry[0].to!uint;
|
|
|
|
import translations.characterBook;
|
|
if(characterId in characterBookFlowerLanguage)
|
|
{
|
|
characterBookEntry[5] = characterBookFlowerLanguage[characterId];
|
|
}
|
|
}
|
|
|
|
foreach(characterCategory; getMasterResponse.masterCharacterCategory)
|
|
{
|
|
auto categoryId = characterCategory[0].to!uint;
|
|
|
|
import translations.characterCategory;
|
|
if(categoryId in characterCategories)
|
|
{
|
|
characterCategory[1] = characterCategories[categoryId];
|
|
}
|
|
}
|
|
|
|
return getMasterResponse.serializeToJson;
|
|
}
|
|
|
|
return json;
|
|
}
|
|
|
|
ubyte[] constructResponseBody(string endpoint, Json json, string nonce)
|
|
{
|
|
import std.zlib : compress;
|
|
|
|
auto jsonStringBytes = cast(ubyte[]) json.toString;
|
|
|
|
switch(endpoint)
|
|
{
|
|
case "/config/getGameConfig":
|
|
auto responsePipe = Pipe
|
|
(
|
|
getCipher
|
|
(
|
|
cipherName,
|
|
SymmetricKey((cast(ubyte[]) keyString).ptr, keyString.length),
|
|
InitializationVector((cast(ubyte[]) nonce).ptr, nonce.length),
|
|
ENCRYPTION
|
|
),
|
|
new Base64Encoder
|
|
);
|
|
|
|
//TODO: Refactor this
|
|
if(jsonStringBytes.length % 16 != 0)
|
|
{
|
|
jsonStringBytes.length += 16 - jsonStringBytes.length % 16;
|
|
}
|
|
|
|
responsePipe.processMsg(jsonStringBytes.ptr, jsonStringBytes.length);
|
|
|
|
return cast(ubyte[]) responsePipe.toString;
|
|
case "/master/getMaster":
|
|
return compress(jsonStringBytes); //TODO: Use pipe instead (how?)
|
|
default:
|
|
auto responsePipe = Pipe
|
|
(
|
|
new Base64Encoder
|
|
);
|
|
|
|
responsePipe.processMsg(jsonStringBytes.ptr, jsonStringBytes.length);
|
|
|
|
return compress(responsePipe.toString);
|
|
}
|
|
}
|
|
|
|
HTTPClientResponse sendRequest(string endpoint, InetHeaderMap headers, ubyte[] requestBody)
|
|
{
|
|
return requestHTTP("http://" ~ serverHost ~ "/api/v1" ~ endpoint,
|
|
(scope HTTPClientRequest r)
|
|
{
|
|
r.method = HTTPMethod.POST;
|
|
r.headers = headers.dup;
|
|
r.writeBody(requestBody, r.headers["Content-Type"]);
|
|
}
|
|
);
|
|
}
|
|
|
|
void sendResponse(HTTPServerResponse response, HTTPClientResponse apiResponse, ubyte[] responseBody)
|
|
{
|
|
if("Transfer-Encoding" in apiResponse.headers) { apiResponse.headers.remove("Transfer-Encoding"); }
|
|
response.statusCode = apiResponse.statusCode;
|
|
response.statusPhrase = apiResponse.statusPhrase;
|
|
response.headers = apiResponse.headers.dup;
|
|
response.writeBody(responseBody);
|
|
}
|