From 2a84f070320612ab2f48f198236c8a66ea483236 Mon Sep 17 00:00:00 2001 From: Konstantin Kalinovsky Date: Mon, 5 Aug 2019 01:57:36 +0200 Subject: [PATCH 01/10] #327 Exe uploading API --- .travis.yml | 2 +- .../deployment/ExeUploadControllerTest.java | 81 ++ src/inttest/resources/config/application.yml | 9 + .../resources/sql/cleanFeaturedMods.sql | 5 + .../resources/sql/prepFeaturedMods.sql | 886 ++++++++++++++++++ src/inttest/resources/sql/prepGameData.sql | 2 + .../api/config/FafApiProperties.java | 4 + .../api/config/swagger/SwaggerConfig.java | 1 + .../api/deployment/ExeUploaderController.java | 59 ++ .../api/deployment/ExeUploaderService.java | 95 ++ .../LegacyFeaturedModDeploymentTask.java | 20 +- .../com/faforever/api/error/ErrorCode.java | 4 +- .../api/featuredmods/FeaturedModService.java | 4 + .../LegacyFeaturedModFileRepository.java | 24 +- src/main/resources/config/application-dev.yml | 3 + .../resources/config/application-prod.yml | 3 + .../deployment/ExeUploaderControllerTest.java | 100 ++ .../deployment/ExeUploaderServiceTest.java | 125 +++ 18 files changed, 1408 insertions(+), 19 deletions(-) create mode 100644 src/inttest/java/com/faforever/api/deployment/ExeUploadControllerTest.java create mode 100644 src/inttest/resources/sql/cleanFeaturedMods.sql create mode 100644 src/inttest/resources/sql/prepFeaturedMods.sql create mode 100644 src/main/java/com/faforever/api/deployment/ExeUploaderController.java create mode 100644 src/main/java/com/faforever/api/deployment/ExeUploaderService.java create mode 100644 src/test/java/com/faforever/api/deployment/ExeUploaderControllerTest.java create mode 100644 src/test/java/com/faforever/api/deployment/ExeUploaderServiceTest.java diff --git a/.travis.yml b/.travis.yml index f1dd9e045..6aa37d22d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ git: env: global: - - GRADLE_CLI_OPTS="--console=plain --stacktrace --quiet" + - GRADLE_CLI_OPTS="--console=plain --stacktrace --full-stacktrace" before_install: - sudo /etc/init.d/mysql stop diff --git a/src/inttest/java/com/faforever/api/deployment/ExeUploadControllerTest.java b/src/inttest/java/com/faforever/api/deployment/ExeUploadControllerTest.java new file mode 100644 index 000000000..af4035a70 --- /dev/null +++ b/src/inttest/java/com/faforever/api/deployment/ExeUploadControllerTest.java @@ -0,0 +1,81 @@ +package com.faforever.api.deployment; + +import com.faforever.api.AbstractIntegrationTest; +import org.junit.Before; +import org.junit.Test; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.context.jdbc.Sql; + +import java.nio.file.Files; +import java.nio.file.Paths; + +import static org.junit.Assert.assertTrue; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.fileUpload; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, scripts = "classpath:sql/prepFeaturedMods.sql") +@Sql(executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, scripts = "classpath:sql/cleanFeaturedMods.sql") +public class ExeUploadControllerTest extends AbstractIntegrationTest { + private MockMultipartFile file; + private static final String SUPER_SECRET = "banana"; + + @Before + public void setUp() { + super.setUp(); + file = new MockMultipartFile("file", "ForgedAlliance.exe", "application/octet-stream", new byte[]{ 1, 2 ,3, 4 }); + } + + @Test + public void testSuccessUploadBeta() throws Exception { + this.mockMvc.perform(fileUpload("/exe/upload") + .file(file) + .param("modName", "fafbeta") + .param("apiKey", SUPER_SECRET) + ).andExpect(status().isOk()); + assertTrue(Files.exists(Paths.get("build/exe/beta/ForgedAlliance.3706.exe"))); + } + + @Test + public void testSuccessUploadDevelop() throws Exception { + this.mockMvc.perform(fileUpload("/exe/upload") + .file(file) + .param("modName", "fafdevelop") + .param("apiKey", SUPER_SECRET) + ).andExpect(status().isOk()); + assertTrue(Files.exists(Paths.get("build/exe/develop/ForgedAlliance.3707.exe"))); + } + + @Test + public void testBadRequestUploadNoModName() throws Exception { + this.mockMvc.perform(fileUpload("/exe/upload") + .file(file) + .param("apiKey", SUPER_SECRET) + ).andExpect(status().is4xxClientError()); + } + + @Test + public void testBadRequestUploadNoFile() throws Exception { + this.mockMvc.perform(fileUpload("/exe/upload") + .param("modName", "fafdevelop") + .param("apiKey", SUPER_SECRET) + ).andExpect(status().is4xxClientError()); + } + + @Test + public void testBadRequestUploadFileWithWrongExeExtension() throws Exception { + MockMultipartFile file = new MockMultipartFile("file", "ForgedAlliance.zip", "application/octet-stream", new byte[]{ 1, 2 ,3, 4 }); + this.mockMvc.perform(fileUpload("/exe/upload") + .file(file) + .param("modName", "fafbeta") + .param("apiKey", SUPER_SECRET) + ).andExpect(status().is4xxClientError()); + } + + @Test + public void testBadRequestUploadWithoutApiKey() throws Exception { + this.mockMvc.perform(fileUpload("/exe/upload") + .file(file) + .param("modName", "fafbeta") + ).andExpect(status().is4xxClientError()); + } +} diff --git a/src/inttest/resources/config/application.yml b/src/inttest/resources/config/application.yml index 2cb9f847c..351409c24 100644 --- a/src/inttest/resources/config/application.yml +++ b/src/inttest/resources/config/application.yml @@ -66,6 +66,15 @@ faf-api: mautic: client-id: banana client-secret: banana + deployment: + featured-mods-target-directory: "build/cache/mods" + repositories-directory: "" + forged-alliance-exe-path: "build/exe/faf" + testing-exe-upload-key: "banana" + forged-alliance-beta-exe-path: "build/exe/beta" + forged-alliance-develop-exe-path: "build/exe/develop" + featured-mod: + file-url-format: "file://%s/%s" logging: diff --git a/src/inttest/resources/sql/cleanFeaturedMods.sql b/src/inttest/resources/sql/cleanFeaturedMods.sql new file mode 100644 index 000000000..c2280ad33 --- /dev/null +++ b/src/inttest/resources/sql/cleanFeaturedMods.sql @@ -0,0 +1,5 @@ +DROP TABLE updates_fafdevelop_files; +DELETE FROM updates_fafbeta_files; +DROP TABLE updates_fafdevelop; +DELETE FROM updates_fafbeta; +DELETE FROM game_featuredMods; diff --git a/src/inttest/resources/sql/prepFeaturedMods.sql b/src/inttest/resources/sql/prepFeaturedMods.sql new file mode 100644 index 000000000..d028ae004 --- /dev/null +++ b/src/inttest/resources/sql/prepFeaturedMods.sql @@ -0,0 +1,886 @@ +DELETE FROM updates_fafbeta_files; +DELETE FROM updates_fafbeta; +DELETE FROM reported_user; +DELETE FROM moderation_report; +DELETE FROM game_stats; +DELETE FROM game_featuredMods; + +DROP TABLE IF EXISTS `updates_fafdevelop_files`; +DROP TABLE IF EXISTS `updates_fafdevelop`; + +CREATE TABLE IF NOT EXISTS `updates_fafdevelop_files` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `fileId` smallint(5) unsigned NOT NULL, + `version` int(11) NOT NULL, + `name` varchar(45) NOT NULL, + `md5` varchar(45) NOT NULL, + `obselete` tinyint(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `fileId` (`fileId`) +) ENGINE=InnoDB; + +CREATE TABLE IF NOT EXISTS `updates_fafdevelop` ( + `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT, + `filename` varchar(45) NOT NULL, + `path` varchar(45) NOT NULL, + UNIQUE KEY `id` (`id`) +) ENGINE=InnoDB; + + + +INSERT INTO `game_featuredMods` (`id`, `gamemod`, `description`, `name`, `publish`, `order`, `git_url`, `git_branch`, `file_extension`, `allow_override`, `deployment_webhook`) VALUES +(1, 'faf', 'Forged Alliance Forever', 'FAF', 1, 0, 'https://github.com/FAForever/fa.git', 'deploy/faf', 'nx2', 0, NULL), +(27, 'fafbeta', 'Beta version of the next FAF patch', 'FAF Beta', 1, 2, 'https://github.com/FAForever/fa.git', 'deploy/fafbeta', 'nx4', 1, NULL), +(28, 'fafdevelop', 'Updated frequently for testing the upcoming game Patch', 'FAF Develop', 1, 11, 'https://github.com/FAForever/fa.git', 'deploy/fafdevelop', 'nx5', 1, NULL); + +INSERT INTO `updates_fafbeta` (`id`, `filename`, `path`) VALUES +(1, 'ForgedAlliance.exe', 'bin'), +(2, 'init_fafbeta.lua', 'bin'), +(11, 'effects.nx4', 'gamedata'), +(12, 'env.nx4', 'gamedata'), +(13, 'loc.nx4', 'gamedata'), +(14, 'lua.nx4', 'gamedata'), +(15, 'meshes.nx4', 'gamedata'), +(17, 'modules.nx4', 'gamedata'), +(18, 'projectiles.nx4', 'gamedata'), +(19, 'schook.nx4', 'gamedata'), +(20, 'textures.nx4', 'gamedata'), +(21, 'units.nx4', 'gamedata'), +(22, 'etc.nx4', 'gamedata'); + +INSERT INTO `updates_fafbeta_files` (`id`, `fileId`, `version`, `name`, `md5`, `obselete`) VALUES +(1, 2, 3652, 'init_fafbeta.lua', '0e58a7ecf1609b6453d62ceeb44f89c9', 0), +(2, 11, 3652, 'effects_0.3652.nxt', '31d289288784211fbf57fb639ce88a64', 0), +(3, 12, 3652, 'env_0.3652.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(4, 13, 3652, 'loc_0.3652.nxt', '97464f661ea84600543bbde05d12dd73', 0), +(5, 14, 3652, 'lua_0.3652.nxt', '7642fea8dfc908b490ec60db67ece06c', 0), +(6, 15, 3652, 'meshes_0.3652.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(7, 17, 3652, 'modules_0.3652.nxt', '6a81c76137fe7fe6579c55aabf03dbb7', 0), +(8, 18, 3652, 'projectiles_0.3652.nxt', 'ecbff75d5ee54dbea7baffec71a214b4', 0), +(9, 19, 3652, 'schook_0.3652.nxt', 'd70eb26a7f9eeddc28df711b839156b0', 0), +(10, 20, 3652, 'textures_0.3652.nxt', '1e0dd68c463f300234423a2346e5a979', 0), +(11, 21, 3652, 'units_0.3652.nxt', '0e91d45f764d27c7924a09384a47e60a', 0), +(12, 22, 3652, 'etc_0.3652.nxt', 'cb38d0bc92e4d01b8afce879bd3ab669', 0), +(24, 11, 3653, 'effects_0.3653.nxt', '7823e173c153ce3fc2977c68c54f1a3c', 0), +(25, 12, 3653, 'env_0.3653.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(26, 13, 3653, 'loc_0.3653.nxt', '85b23808ab6478b2fd303da8248bf738', 0), +(27, 14, 3653, 'lua_0.3653.nxt', 'f72663f46e1b15ec45314221a82b4d77', 0), +(28, 15, 3653, 'meshes_0.3653.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(29, 17, 3653, 'modules_0.3653.nxt', 'e20fec02e2251b5085cbe4096b7769a9', 0), +(30, 18, 3653, 'projectiles_0.3653.nxt', 'c1a7b415a3f5477470150e91580ccf19', 0), +(31, 19, 3653, 'schook_0.3653.nxt', 'fc661665d45af0976f5a70de863046a2', 0), +(32, 20, 3653, 'textures_0.3653.nxt', 'b72caa7d4e76ff71471badf55b8ee800', 0), +(33, 21, 3653, 'units_0.3653.nxt', '1255955a81ee7d73862ed6a54fb0d42f', 0), +(34, 22, 3653, 'etc_0.3653.nxt', '3277eb482e12fe0231fe692f356d935b', 0), +(35, 11, 3654, 'effects_0.3654.nxt', '7823e173c153ce3fc2977c68c54f1a3c', 0), +(36, 12, 3654, 'env_0.3654.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(37, 13, 3654, 'loc_0.3654.nxt', '6e9f6a5e148e94a8a292ed58d13f90d4', 0), +(38, 14, 3654, 'lua_0.3654.nxt', 'aaae2e5185b8ccb555296cb36f5af460', 0), +(39, 15, 3654, 'meshes_0.3654.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(40, 17, 3654, 'modules_0.3654.nxt', 'e20fec02e2251b5085cbe4096b7769a9', 0), +(41, 18, 3654, 'projectiles_0.3654.nxt', 'c1a7b415a3f5477470150e91580ccf19', 0), +(42, 19, 3654, 'schook_0.3654.nxt', 'fc661665d45af0976f5a70de863046a2', 0), +(43, 20, 3654, 'textures_0.3654.nxt', 'b72caa7d4e76ff71471badf55b8ee800', 0), +(44, 21, 3654, 'units_0.3654.nxt', '45e8a56d3d8f5750611961459f191c7a', 0), +(45, 22, 3654, 'etc_0.3654.nxt', '3277eb482e12fe0231fe692f356d935b', 0), +(46, 11, 3655, 'effects_0.3655.nxt', '7823e173c153ce3fc2977c68c54f1a3c', 0), +(47, 12, 3655, 'env_0.3655.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(48, 13, 3655, 'loc_0.3655.nxt', '6e9f6a5e148e94a8a292ed58d13f90d4', 0), +(49, 14, 3655, 'lua_0.3655.nxt', '5b34a8b361448cd3f515c96c4faca15b', 0), +(50, 15, 3655, 'meshes_0.3655.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(51, 17, 3655, 'modules_0.3655.nxt', 'e20fec02e2251b5085cbe4096b7769a9', 0), +(52, 18, 3655, 'projectiles_0.3655.nxt', 'c1a7b415a3f5477470150e91580ccf19', 0), +(53, 19, 3655, 'schook_0.3655.nxt', '122e008515f6a33e2d7936650373423e', 0), +(54, 20, 3655, 'textures_0.3655.nxt', 'ad4c595b8c0a0fb8b0e1e78ccf07c192', 0), +(55, 21, 3655, 'units_0.3655.nxt', '6fc20c5699bef0d18691fdf62cbb09d3', 0), +(56, 22, 3655, 'etc_0.3655.nxt', '3514201b87653551cf5db696556ec475', 0), +(57, 11, 3656, 'effects_0.3656.nxt', '7823e173c153ce3fc2977c68c54f1a3c', 0), +(58, 12, 3656, 'env_0.3656.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(59, 13, 3656, 'loc_0.3656.nxt', '670934cd424bc14e85c389c73e9f4175', 0), +(60, 14, 3656, 'lua_0.3656.nxt', '6dd341f362de25c336eaf963db1e2cad', 0), +(61, 15, 3656, 'meshes_0.3656.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(62, 17, 3656, 'modules_0.3656.nxt', 'e20fec02e2251b5085cbe4096b7769a9', 0), +(63, 18, 3656, 'projectiles_0.3656.nxt', 'c1a7b415a3f5477470150e91580ccf19', 0), +(64, 19, 3656, 'schook_0.3656.nxt', '122e008515f6a33e2d7936650373423e', 0), +(65, 20, 3656, 'textures_0.3656.nxt', 'ad4c595b8c0a0fb8b0e1e78ccf07c192', 0), +(66, 21, 3656, 'units_0.3656.nxt', 'a6413d206fc9db539bbe543adfc70b0f', 0), +(67, 22, 3656, 'etc_0.3656.nxt', '3514201b87653551cf5db696556ec475', 0), +(68, 11, 3657, 'effects_0.3657.nxt', '7823e173c153ce3fc2977c68c54f1a3c', 0), +(69, 12, 3657, 'env_0.3657.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(70, 13, 3657, 'loc_0.3657.nxt', '670934cd424bc14e85c389c73e9f4175', 0), +(71, 14, 3657, 'lua_0.3657.nxt', '225aefd5d4baa4e50c758cb06b880bb1', 0), +(72, 15, 3657, 'meshes_0.3657.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(73, 17, 3657, 'modules_0.3657.nxt', 'e20fec02e2251b5085cbe4096b7769a9', 0), +(74, 18, 3657, 'projectiles_0.3657.nxt', 'c1a7b415a3f5477470150e91580ccf19', 0), +(75, 19, 3657, 'schook_0.3657.nxt', '122e008515f6a33e2d7936650373423e', 0), +(76, 20, 3657, 'textures_0.3657.nxt', 'ad4c595b8c0a0fb8b0e1e78ccf07c192', 0), +(77, 21, 3657, 'units_0.3657.nxt', 'a6413d206fc9db539bbe543adfc70b0f', 0), +(78, 22, 3657, 'etc_0.3657.nxt', '3514201b87653551cf5db696556ec475', 0), +(79, 11, 3658, 'effects_0.3658.nxt', '7823e173c153ce3fc2977c68c54f1a3c', 0), +(80, 12, 3658, 'env_0.3658.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(81, 13, 3658, 'loc_0.3658.nxt', '670934cd424bc14e85c389c73e9f4175', 0), +(82, 14, 3658, 'lua_0.3658.nxt', 'b0f6c1a40bbc41e398ee9340ea14dd1f', 0), +(83, 15, 3658, 'meshes_0.3658.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(84, 17, 3658, 'modules_0.3658.nxt', 'e20fec02e2251b5085cbe4096b7769a9', 0), +(85, 18, 3658, 'projectiles_0.3658.nxt', 'c1a7b415a3f5477470150e91580ccf19', 0), +(86, 19, 3658, 'schook_0.3658.nxt', '122e008515f6a33e2d7936650373423e', 0), +(87, 20, 3658, 'textures_0.3658.nxt', 'ad4c595b8c0a0fb8b0e1e78ccf07c192', 0), +(88, 21, 3658, 'units_0.3658.nxt', 'a6413d206fc9db539bbe543adfc70b0f', 0), +(89, 22, 3658, 'etc_0.3658.nxt', '3514201b87653551cf5db696556ec475', 0), +(280, 11, 3659, 'effects_0.3659.nxt', '3758baad77531dd5323c766433412e91', 0), +(281, 12, 3659, 'env_0.3659.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(282, 13, 3659, 'loc_0.3659.nxt', 'caf6f9cf4909163ca37ecf1956874865', 0), +(283, 14, 3659, 'lua_0.3659.nxt', '5f557facb06c9006126b5be2a4881798', 0), +(284, 15, 3659, 'meshes_0.3659.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(285, 17, 3659, 'modules_0.3659.nxt', '89cc1c9c5f6a99a59966aaf30090bea6', 0), +(286, 18, 3659, 'projectiles_0.3659.nxt', '0ba261e8f444dfe7f4e12e748e5f159c', 0), +(287, 19, 3659, 'schook_0.3659.nxt', 'b9b6fe2c89d614d402a0335cf3e2fcba', 0), +(288, 20, 3659, 'textures_0.3659.nxt', 'f275298accf9552a6604ed62306add72', 0), +(289, 21, 3659, 'units_0.3659.nxt', '524b476ad5f64e36d9ddfe0a91a2c932', 0), +(290, 22, 3659, 'etc_0.3659.nxt', '3514201b87653551cf5db696556ec475', 0), +(434, 11, 3660, 'effects_0.3660.nxt', 'ce6d16a8ebd7f058a27f5ab6c0787530', 0), +(435, 12, 3660, 'env_0.3660.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(436, 13, 3660, 'loc_0.3660.nxt', '576fee631af11d7d4eddc5adcfd10dfa', 0), +(437, 14, 3660, 'lua_0.3660.nxt', '31a6f3014812c06f8979bce1e0903b4e', 0), +(438, 15, 3660, 'meshes_0.3660.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(439, 17, 3660, 'modules_0.3660.nxt', 'd39bb3e29bcc1fef41c0e47a705b6bb9', 0), +(440, 18, 3660, 'projectiles_0.3660.nxt', '28b6453e1e432985be201fd7bdde4250', 0), +(441, 19, 3660, 'schook_0.3660.nxt', 'a51b4f97dbdec1ebb7151f47340a0baa', 0), +(442, 20, 3660, 'textures_0.3660.nxt', '87e3ff26c66d3792c0b51b67c2c6487f', 0), +(443, 21, 3660, 'units_0.3660.nxt', '1ab896098a9b8206b5a15007ca7a5b98', 0), +(444, 22, 3660, 'etc_0.3660.nxt', '3514201b87653551cf5db696556ec475', 0), +(467, 11, 3662, 'effects_0.3662.nxt', 'ce6d16a8ebd7f058a27f5ab6c0787530', 0), +(468, 12, 3662, 'env_0.3662.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(469, 13, 3662, 'loc_0.3662.nxt', 'e539fff338506a2dd60383814ba06ade', 0), +(470, 14, 3662, 'lua_0.3662.nxt', 'e182bfcba5f074e64740a45dff354013', 0), +(471, 15, 3662, 'meshes_0.3662.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(472, 17, 3662, 'modules_0.3662.nxt', 'd39bb3e29bcc1fef41c0e47a705b6bb9', 0), +(473, 18, 3662, 'projectiles_0.3662.nxt', '0ba261e8f444dfe7f4e12e748e5f159c', 0), +(474, 19, 3662, 'schook_0.3662.nxt', 'a51b4f97dbdec1ebb7151f47340a0baa', 0), +(475, 20, 3662, 'textures_0.3662.nxt', '87e3ff26c66d3792c0b51b67c2c6487f', 0), +(476, 21, 3662, 'units_0.3662.nxt', '6610ef6f832d8f5361053d239aeaec10', 0), +(477, 22, 3662, 'etc_0.3662.nxt', '5d334af582b719f05c963635e1f524df', 0), +(498, 11, 3663, 'effects_0.3663.nxt', 'ce6d16a8ebd7f058a27f5ab6c0787530', 0), +(499, 12, 3663, 'env_0.3663.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(500, 13, 3663, 'loc_0.3663.nxt', 'e539fff338506a2dd60383814ba06ade', 0), +(501, 14, 3663, 'lua_0.3663.nxt', '2f4e4a48f3bead03d698484fac1ae412', 0), +(502, 15, 3663, 'meshes_0.3663.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(503, 17, 3663, 'modules_0.3663.nxt', 'd39bb3e29bcc1fef41c0e47a705b6bb9', 0), +(504, 18, 3663, 'projectiles_0.3663.nxt', '0ba261e8f444dfe7f4e12e748e5f159c', 0), +(505, 19, 3663, 'schook_0.3663.nxt', 'a51b4f97dbdec1ebb7151f47340a0baa', 0), +(506, 20, 3663, 'textures_0.3663.nxt', '87e3ff26c66d3792c0b51b67c2c6487f', 0), +(507, 21, 3663, 'units_0.3663.nxt', 'e9968b6985b1197c6df3d0d8ad03b2d6', 0), +(508, 22, 3663, 'etc_0.3663.nxt', '5d334af582b719f05c963635e1f524df', 0), +(509, 1, 3663, 'ForgedAlliance.3663.exe', '647693d2c680ff5177741aeeebbe9b5b', 0), +(510, 11, 3664, 'effects_0.3664.nxt', 'ce6d16a8ebd7f058a27f5ab6c0787530', 0), +(511, 12, 3664, 'env_0.3664.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(512, 13, 3664, 'loc_0.3664.nxt', '9e9fb4703603e3661161f291a4c77134', 0), +(513, 14, 3664, 'lua_0.3664.nxt', '6087597c9f71afd23dec5dd4bd1a1eeb', 0), +(514, 15, 3664, 'meshes_0.3664.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(515, 17, 3664, 'modules_0.3664.nxt', 'd39bb3e29bcc1fef41c0e47a705b6bb9', 0), +(516, 18, 3664, 'projectiles_0.3664.nxt', '28b6453e1e432985be201fd7bdde4250', 0), +(517, 19, 3664, 'schook_0.3664.nxt', 'a51b4f97dbdec1ebb7151f47340a0baa', 0), +(518, 20, 3664, 'textures_0.3664.nxt', '87e3ff26c66d3792c0b51b67c2c6487f', 0), +(519, 21, 3664, 'units_0.3664.nxt', 'bd46fa7db35df0d2602af6da599c8b27', 0), +(520, 22, 3664, 'etc_0.3664.nxt', '5d334af582b719f05c963635e1f524df', 0), +(521, 1, 3664, 'ForgedAlliance.3664.exe', '37d717431bcd8549cebb74541ddb64fa', 0), +(522, 11, 3665, 'effects_0.3665.nxt', 'ce6d16a8ebd7f058a27f5ab6c0787530', 0), +(523, 12, 3665, 'env_0.3665.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(524, 13, 3665, 'loc_0.3665.nxt', '9e9fb4703603e3661161f291a4c77134', 0), +(525, 14, 3665, 'lua_0.3665.nxt', '6087597c9f71afd23dec5dd4bd1a1eeb', 0), +(526, 15, 3665, 'meshes_0.3665.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(527, 17, 3665, 'modules_0.3665.nxt', 'd39bb3e29bcc1fef41c0e47a705b6bb9', 0), +(528, 18, 3665, 'projectiles_0.3665.nxt', '28b6453e1e432985be201fd7bdde4250', 0), +(529, 19, 3665, 'schook_0.3665.nxt', 'a51b4f97dbdec1ebb7151f47340a0baa', 0), +(530, 20, 3665, 'textures_0.3665.nxt', '87e3ff26c66d3792c0b51b67c2c6487f', 0), +(531, 21, 3665, 'units_0.3665.nxt', 'bd46fa7db35df0d2602af6da599c8b27', 0), +(532, 22, 3665, 'etc_0.3665.nxt', '5d334af582b719f05c963635e1f524df', 0), +(533, 1, 3665, 'ForgedAlliance.3665.exe', 'f7b6ce9f2decccc86462bc971df6daf1', 0), +(537, 11, 3666, 'effects_0.3666.nxt', '4515b175e2b827131871ccbd72016c76', 0), +(538, 12, 3666, 'env_0.3666.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(539, 13, 3666, 'loc_0.3666.nxt', 'bd29abc6e7370990be1697ff4545c7df', 0), +(540, 14, 3666, 'lua_0.3666.nxt', 'fb8ef62f0bf70b06edf020c68150ce26', 0), +(541, 15, 3666, 'meshes_0.3666.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(542, 17, 3666, 'modules_0.3666.nxt', 'ef0d9fba50c0adaa1e4615c5fcf3c309', 0), +(543, 18, 3666, 'projectiles_0.3666.nxt', '0ba261e8f444dfe7f4e12e748e5f159c', 0), +(544, 19, 3666, 'schook_0.3666.nxt', 'ce96f20d4bf19d8a231e97ac86e674fa', 0), +(545, 20, 3666, 'textures_0.3666.nxt', 'c6ad21d72c6c937d23a35af4eda78dc4', 0), +(546, 21, 3666, 'units_0.3666.nxt', '6383db6ee0f463dd340972d44e3ad7fa', 0), +(547, 22, 3666, 'etc_0.3666.nxt', '0903a5a32cb70c564349dcfe84fba9a7', 0), +(548, 1, 3666, 'ForgedAlliance.3666.exe', '8036f2d3f3d0670e96822bc4e2ae6d4d', 0), +(549, 11, 3667, 'effects_0.3667.nxt', '4515b175e2b827131871ccbd72016c76', 0), +(550, 12, 3667, 'env_0.3667.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(551, 13, 3667, 'loc_0.3667.nxt', 'bd29abc6e7370990be1697ff4545c7df', 0), +(552, 14, 3667, 'lua_0.3667.nxt', 'fb8ef62f0bf70b06edf020c68150ce26', 0), +(553, 15, 3667, 'meshes_0.3667.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(554, 17, 3667, 'modules_0.3667.nxt', 'ef0d9fba50c0adaa1e4615c5fcf3c309', 0), +(555, 18, 3667, 'projectiles_0.3667.nxt', '0ba261e8f444dfe7f4e12e748e5f159c', 0), +(556, 19, 3667, 'schook_0.3667.nxt', 'ce96f20d4bf19d8a231e97ac86e674fa', 0), +(557, 20, 3667, 'textures_0.3667.nxt', 'c6ad21d72c6c937d23a35af4eda78dc4', 0), +(558, 21, 3667, 'units_0.3667.nxt', '6383db6ee0f463dd340972d44e3ad7fa', 0), +(559, 22, 3667, 'etc_0.3667.nxt', '0903a5a32cb70c564349dcfe84fba9a7', 0), +(560, 1, 3667, 'ForgedAlliance.3667.exe', 'de8492bf49b6a980f5745db6892e3884', 0), +(561, 11, 3672, 'effects_0.3672.nxt', '4515b175e2b827131871ccbd72016c76', 0), +(562, 12, 3672, 'env_0.3672.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(563, 13, 3672, 'loc_0.3672.nxt', 'bd29abc6e7370990be1697ff4545c7df', 0), +(564, 14, 3672, 'lua_0.3672.nxt', '47817bab524c17ce77176aa24eda7fe8', 0), +(565, 15, 3672, 'meshes_0.3672.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(566, 17, 3672, 'modules_0.3672.nxt', 'ef0d9fba50c0adaa1e4615c5fcf3c309', 0), +(567, 18, 3672, 'projectiles_0.3672.nxt', '28b6453e1e432985be201fd7bdde4250', 0), +(568, 19, 3672, 'schook_0.3672.nxt', 'ce96f20d4bf19d8a231e97ac86e674fa', 0), +(569, 20, 3672, 'textures_0.3672.nxt', 'c6ad21d72c6c937d23a35af4eda78dc4', 0), +(570, 21, 3672, 'units_0.3672.nxt', '0d8e9aa06471a3d12fd1acac54343040', 0), +(571, 22, 3672, 'etc_0.3672.nxt', '0903a5a32cb70c564349dcfe84fba9a7', 0), +(572, 1, 3672, 'ForgedAlliance.3672.exe', '6f60f0fae2ffa287c623d90743aa4ab4', 0), +(573, 11, 3675, 'effects_0.3675.nxt', '4515b175e2b827131871ccbd72016c76', 0), +(574, 12, 3675, 'env_0.3675.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(575, 13, 3675, 'loc_0.3675.nxt', 'f6fe4ab343455c24248d67ead54dee33', 0), +(576, 14, 3675, 'lua_0.3675.nxt', '9f0a8b4a8b3562ca97f75ae8cd94ccee', 0), +(577, 15, 3675, 'meshes_0.3675.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(578, 17, 3675, 'modules_0.3675.nxt', '4c91eadddbf40122f8cd09283fdc1683', 0), +(579, 18, 3675, 'projectiles_0.3675.nxt', 'e2647c7ff11599d22c568c0cd1248d89', 0), +(580, 19, 3675, 'schook_0.3675.nxt', 'ce96f20d4bf19d8a231e97ac86e674fa', 0), +(581, 20, 3675, 'textures_0.3675.nxt', 'c6ad21d72c6c937d23a35af4eda78dc4', 0), +(582, 21, 3675, 'units_0.3675.nxt', '88a9f1cec5d00a446068ecda94f360ce', 0), +(583, 22, 3675, 'etc_0.3675.nxt', '2bf5a88f0cd370dcc037a96627ecc81e', 0), +(584, 1, 3675, 'ForgedAlliance.3675.exe', '54ad49b7838abfc1d498783614a0176a', 0), +(633, 11, 3676, 'effects_0.3676.nxt', '4515b175e2b827131871ccbd72016c76', 0), +(634, 12, 3676, 'env_0.3676.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(635, 13, 3676, 'loc_0.3676.nxt', '13df257b7367637c14082c9906a16408', 0), +(636, 14, 3676, 'lua_0.3676.nxt', '3215a1392a0ef76ce7b4ed454b59594c', 0), +(637, 15, 3676, 'meshes_0.3676.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(638, 17, 3676, 'modules_0.3676.nxt', 'a38366bd1e20c21ea0a92f3c8fde5d97', 0), +(639, 18, 3676, 'projectiles_0.3676.nxt', '28b6453e1e432985be201fd7bdde4250', 0), +(640, 19, 3676, 'schook_0.3676.nxt', 'ce96f20d4bf19d8a231e97ac86e674fa', 0), +(641, 20, 3676, 'textures_0.3676.nxt', '5ae4cbd544204a93a5c03eec2a4b7ff6', 0), +(642, 21, 3676, 'units_0.3676.nxt', 'f3b20444f90c0c423a89ab11eaf24e16', 0), +(643, 22, 3676, 'etc_0.3676.nxt', '6f720d8261abf03f6c77f1971e5be04f', 0), +(644, 1, 3676, 'ForgedAlliance.3676.exe', 'cb9d3ef03be5800ce761c9e4914d9b86', 0), +(801, 22, 3678, 'etc.3678.nxt', 'b918ac7a67d049d5a76d04e059c3015d', 0), +(802, 12, 3678, 'env.3678.nxt', 'bb1644d1ba5baa07d63395ce76315d81', 0), +(803, 17, 3678, 'modules.3678.nxt', 'e6c6cd72f3f8740527cf8889622f5c18', 0), +(804, 11, 3678, 'effects.3678.nxt', '35a6f614efcc554b0eb35ecd551a944f', 0), +(805, 21, 3678, 'units.3678.nxt', 'a1fda89aba07a5187364ef17fb954241', 0), +(806, 15, 3678, 'meshes.3678.nxt', '164be3f0c7c256e5eee37248d87f7342', 0), +(807, 18, 3678, 'projectiles.3678.nxt', '4bc9d87157859bbded8b1a9ec070ba3c', 0), +(808, 19, 3678, 'schook.3678.nxt', '7cea700765db6332db24bc2a4c381e62', 0), +(809, 14, 3678, 'lua.3678.nxt', '3203164a25199be4be811a47ded056b8', 0), +(810, 13, 3678, 'loc.3678.nxt', '6f9f332721b848258740350f80513503', 0), +(811, 20, 3678, 'textures.3678.nxt', '3a2fa4f900f2abab3ef932f4d2679fa6', 0), +(812, 1, 3678, 'ForgedAlliance.3678.exe', 'c8e6afa7799743ef1d32b6e2fea7cfca', 0), +(868, 22, 3682, 'etc.3682.nx4', 'fe9fb46249ce718e21fe5cb1c85e646e', 0), +(869, 12, 3682, 'env.3682.nx4', 'f6724caf818c4acbb69f4f060f6a273b', 0), +(870, 11, 3682, 'effects.3682.nx4', 'd07346f3328e28e3a808aa63a7d5eb26', 0), +(871, 21, 3682, 'units.3682.nx4', 'a31febda4db2a5774cf1815babda161f', 0), +(872, 15, 3682, 'meshes.3682.nx4', 'ed02aec5422e6f5e54bd1f807ed86acb', 0), +(873, 18, 3682, 'projectiles.3682.nx4', '03bf9dc392443d275daccf41c834a09a', 0), +(874, 19, 3682, 'schook.3682.nx4', '588e206656ef632c59c8687ff20046d1', 0), +(875, 14, 3682, 'lua.3682.nx4', '9fa642d450431ec5592d07ee19894cbf', 0), +(876, 13, 3682, 'loc.3682.nx4', '0840afc4d84e5a56d7cd8e376af448b5', 0), +(877, 20, 3682, 'textures.3682.nx4', 'bb89ed7b678e1f6a4be672753e00939b', 0), +(878, 1, 3682, 'ForgedAlliance.3682.exe', '334e86d0e0531e3539b1e3c8111e452c', 0), +(901, 22, 3684, 'etc.3684.nx4', 'ac54bf8d21ecd70d507fce4d11bb1fb3', 0), +(902, 12, 3684, 'env.3684.nx4', 'c1c3bd39895c2a84eccf9863bc7fd530', 0), +(903, 11, 3684, 'effects.3684.nx4', '97612e56f7fd98cc87ebb43b0e2b2e19', 0), +(904, 21, 3684, 'units.3684.nx4', '42d1406e905d786eb7a1dbfbf008f2b2', 0), +(905, 15, 3684, 'meshes.3684.nx4', 'bc6e29201df7203c0d73f51f2eb0f19a', 0), +(906, 18, 3684, 'projectiles.3684.nx4', 'd0254c186be7e787aef4ef13425675e0', 0), +(907, 19, 3684, 'schook.3684.nx4', '48395afd35915435f8b9487107b269e5', 0), +(908, 14, 3684, 'lua.3684.nx4', 'e91fccccfe88710eed13de90ee0d0170', 0), +(909, 13, 3684, 'loc.3684.nx4', 'f9b2f1d42c647600139b8f999a1f0587', 0), +(910, 20, 3684, 'textures.3684.nx4', 'a54956ff44be6f8511bdbe2186674bfe', 0), +(911, 1, 3684, 'ForgedAlliance.3684.exe', '0f9e085d44da00e0ce314c181cf087a8', 0), +(978, 22, 3686, 'etc.3686.nx4', 'b739a7cfa4a6eb04b8e7f3863d1e568e', 0), +(979, 12, 3686, 'env.3686.nx4', 'abb0a29cc31865fb2ab5110940a62b6a', 0), +(980, 11, 3686, 'effects.3686.nx4', 'de42aa8870c23cd991f6350400e241ce', 0), +(981, 21, 3686, 'units.3686.nx4', '0d9448fcba34171f187d820fe7091bc2', 0), +(982, 15, 3686, 'meshes.3686.nx4', 'ffcb971fe8db79495eeb19b746f6480b', 0), +(983, 18, 3686, 'projectiles.3686.nx4', '2cfba94b9710d2f0ca428fd8d9faf6d2', 0), +(984, 19, 3686, 'schook.3686.nx4', '2c4470dd098cbbbce6610ba2c33ec43b', 0), +(985, 14, 3686, 'lua.3686.nx4', '8417b039fd7414dc1dfb579f8ba195a1', 0), +(986, 13, 3686, 'loc.3686.nx4', '6d5ad58090696a79a778c3c335ed8b6e', 0), +(987, 20, 3686, 'textures.3686.nx4', 'ccb2783e34b8d790da4c8625f4477a44', 0), +(988, 1, 3686, 'ForgedAlliance.3686.exe', '4d939f813fcf987435669719e703ca89', 0), +(1011, 12, 3689, 'env.3689.nx4', 'cd2d44a798c1608b5164f9565fbd0052', 0), +(1012, 15, 3689, 'meshes.3689.nx4', '3c9048754067ce46e0ffe6e6d80d1b9e', 0), +(1013, 19, 3689, 'schook.3689.nx4', 'aba8a4e936101628c802f8c429337d0f', 0), +(1014, 14, 3689, 'lua.3689.nx4', '85c17df93d5fbdad59d28f574502ae11', 0), +(1015, 22, 3689, 'etc.3689.nx4', '28935ce19a8c58af10c4bdff7901bd03', 0), +(1016, 11, 3689, 'effects.3689.nx4', '24b74da32c034660d40e86024de629d9', 0), +(1017, 20, 3689, 'textures.3689.nx4', '12eb315add65298044b964cec3e7c5cd', 0), +(1018, 18, 3689, 'projectiles.3689.nx4', 'e80f607016aa70fd6d97be550aae6cdb', 0), +(1019, 13, 3689, 'loc.3689.nx4', '2463fc1010c18d893b48eef95dce9180', 0), +(1020, 21, 3689, 'units.3689.nx4', '3fce3ca6df0fe97603a3656eaaec3d9e', 0), +(1021, 1, 3689, 'ForgedAlliance.3689.exe', '040147d8af136d4202fbda55542c9f2e', 0), +(1110, 12, 3695, 'env.3695.nx4', '57fc03a5506012143b0cb69d11634292', 0), +(1111, 15, 3695, 'meshes.3695.nx4', '49b9db1cc6241536da17fe22564986e5', 0), +(1112, 19, 3695, 'schook.3695.nx4', 'fec8dc5bc9c389ce05de0ea8cab070dd', 0), +(1113, 14, 3695, 'lua.3695.nx4', 'd646596d272198d09bd55d9a90094c25', 0), +(1114, 22, 3695, 'etc.3695.nx4', 'ba7c51bfb81e4e76ff72a822954c9e55', 0), +(1115, 11, 3695, 'effects.3695.nx4', 'd7b619f97c58f1233fa97eebf97dd528', 0), +(1116, 20, 3695, 'textures.3695.nx4', '22f7140bed0250b6a83f0c1040cc0d16', 0), +(1117, 18, 3695, 'projectiles.3695.nx4', 'ec5a0cc9130bcc642657ef9fccca0c49', 0), +(1118, 13, 3695, 'loc.3695.nx4', 'cc4282b2a22f82e77c00eff1dfe7cab8', 0), +(1119, 21, 3695, 'units.3695.nx4', '84dae2a7425f1fdfbf7efecb6c14bfb5', 0), +(1120, 1, 3695, 'ForgedAlliance.3695.exe', 'd8b89bd62950a6a2086b54a2370475e4', 0), +(1132, 12, 3696, 'env.3696.nx4', '1f22fe7781db0b9a1bdf2006e3bc3669', 0), +(1133, 15, 3696, 'meshes.3696.nx4', 'c0c9f7b884eaa080b3609199ca97fa6b', 0), +(1134, 19, 3696, 'schook.3696.nx4', 'f26381597b89f2b99d197ef161075948', 0), +(1135, 14, 3696, 'lua.3696.nx4', 'b5bfe3a95d795f8abc371af1aca70b9d', 0), +(1136, 22, 3696, 'etc.3696.nx4', '9c11ee07d979555b0bfd67c5ccbc9418', 0), +(1137, 11, 3696, 'effects.3696.nx4', '425cef6410d952f06295cec4e14b9882', 0), +(1138, 20, 3696, 'textures.3696.nx4', 'e6fad37a67ba4da7862ae1606ed02267', 0), +(1139, 18, 3696, 'projectiles.3696.nx4', '9e2128da5d631a55ad2a61b553e03025', 0), +(1140, 13, 3696, 'loc.3696.nx4', '7856ca3cf7582a9921e9edc81d892e7f', 0), +(1141, 21, 3696, 'units.3696.nx4', '6edb2aef5a64e479cd0db4bd421462b0', 0), +(1142, 1, 3696, 'ForgedAlliance.3696.exe', 'd95f9d1d70f0fc941f8f5c90c55e2a94', 0), +(1275, 12, 3698, 'env.3698.nx4', '083177c34a323ce7a0208eee5f1f8e1f', 0), +(1276, 15, 3698, 'meshes.3698.nx4', 'b9476380010780e7c3ed80f9064b8d53', 0), +(1277, 19, 3698, 'schook.3698.nx4', '7cf315bfcbb7b70533811fcd2e9ea1bf', 0), +(1278, 14, 3698, 'lua.3698.nx4', 'c8a7453cc221967a252310ca788fc393', 0), +(1279, 22, 3698, 'etc.3698.nx4', '33ff960fd6247089ed3e9f18792bcd30', 0), +(1280, 11, 3698, 'effects.3698.nx4', '6a152989c8e595d782ddb9f7b7d7acdc', 0), +(1281, 20, 3698, 'textures.3698.nx4', '1f6d6ec857c3d32dc25ee0ceda61a14d', 0), +(1282, 18, 3698, 'projectiles.3698.nx4', '38b6ad1354831e927e537e0cdbea287a', 0), +(1283, 13, 3698, 'loc.3698.nx4', '842c5dee64e7a7fe0b6d041f350a973f', 0), +(1284, 21, 3698, 'units.3698.nx4', '480061965eedebbd33efaaad9a854e12', 0), +(1285, 1, 3698, 'ForgedAlliance.3698.exe', 'c0f936b1c9675f57b688ad00a5c486de', 0), +(1341, 12, 3702, 'env.3702.nx4', '9a68e7396d91319c230f13996bad7035', 0), +(1342, 15, 3702, 'meshes.3702.nx4', 'f1320982025e92a2a8e30b07e10c98e8', 0), +(1343, 19, 3702, 'schook.3702.nx4', 'd9fba5cbc485c007335e5c6632b24732', 0), +(1344, 14, 3702, 'lua.3702.nx4', '8bd63fa67251dab33cabac863adabb08', 0), +(1345, 22, 3702, 'etc.3702.nx4', 'e26b531b1dd1644928d561966f0fae7e', 0), +(1346, 11, 3702, 'effects.3702.nx4', '3061a0d6abad344bb4ae93ea8f07bfa2', 0), +(1347, 20, 3702, 'textures.3702.nx4', '72eae59751ac54225a3dc4b9aaef1db4', 0), +(1348, 18, 3702, 'projectiles.3702.nx4', 'daed2d926fbe28ea2dcd25f104d9aa49', 0), +(1349, 13, 3702, 'loc.3702.nx4', 'dfb72d07d3096f74edf7b7c328a5f089', 0), +(1350, 21, 3702, 'units.3702.nx4', 'a80d3078e5484615b0845abfc78d1afa', 0), +(1351, 1, 3702, 'ForgedAlliance.3702.exe', '819558d8cf924dce94c038882e6ed229', 0), +(1649, 12, 3703, 'env.3703.nx4', 'a7fb2a4aed335e755cf04f4437083218', 0), +(1650, 15, 3703, 'meshes.3703.nx4', 'b5fe9aad7f12b399be325325dcd295ab', 0), +(1651, 19, 3703, 'schook.3703.nx4', '0086a01969af84bd0ea21d83b9ce035c', 0), +(1652, 14, 3703, 'lua.3703.nx4', 'f87efecbea867cf574881a45ff248153', 0), +(1653, 22, 3703, 'etc.3703.nx4', '7c24e89089867fd47d18f1f1c735f2c4', 0), +(1654, 11, 3703, 'effects.3703.nx4', '2f11328e51008e87f1fa9648e1e7b9c6', 0), +(1655, 20, 3703, 'textures.3703.nx4', 'b901e8ab925c1e843b9b267a502ccbe2', 0), +(1656, 18, 3703, 'projectiles.3703.nx4', '0e8e0857f132a72d56c066060c8af267', 0), +(1657, 13, 3703, 'loc.3703.nx4', 'f3f3675b90bcaaa04841a3f6320044d2', 0), +(1658, 21, 3703, 'units.3703.nx4', '2d2f2a139f8b3c30e5b9cc19b449f5f3', 0), +(1659, 1, 3703, 'ForgedAlliance.3703.exe', '80d1355256229ab53ba6f24db1fd6a7f', 0), +(1682, 12, 3704, 'env.3704.nx4', 'f5749a9f7be116e79f826a17c418178f', 0), +(1683, 15, 3704, 'meshes.3704.nx4', '9a35fd2bc616476623fac1a87189aac0', 0), +(1684, 19, 3704, 'schook.3704.nx4', '0511820a40e11e68b104fc4189fb35d7', 0), +(1685, 14, 3704, 'lua.3704.nx4', '620bbc301aeac6d83890c353bfc3e3b2', 0), +(1686, 22, 3704, 'etc.3704.nx4', 'd22810e53e40a1f1138a6110f52b5f39', 0), +(1687, 11, 3704, 'effects.3704.nx4', '7dfb96d1447e2d1b9bc44c580457e42e', 0), +(1688, 20, 3704, 'textures.3704.nx4', 'b3b956b95fe98970670259effdebe350', 0), +(1689, 18, 3704, 'projectiles.3704.nx4', 'e9ccf0d21c6a7acda79d177638e8c59d', 0), +(1690, 13, 3704, 'loc.3704.nx4', '3df005b94b84602896cba5ef37648a25', 0), +(1691, 21, 3704, 'units.3704.nx4', '26729adb4c7211ff1dac4b29412b368b', 0), +(1692, 1, 3704, 'ForgedAlliance.3704.exe', '5ff2a30f1225d3396409661c5043c2c2', 0), +(1693, 12, 3706, 'env.3706.nx4', '8d832a91df87847dbca8b5c306795b29', 0), +(1694, 15, 3706, 'meshes.3706.nx4', 'a75e096268f58d89c452c6c63e5f33c0', 0), +(1695, 19, 3706, 'schook.3706.nx4', '52a255058a998abd9dbdeb2f52dd2309', 0), +(1696, 14, 3706, 'lua.3706.nx4', 'ff9d0ee59a2caf1c5cc98cb51aa28048', 0), +(1697, 22, 3706, 'etc.3706.nx4', '9e869dec0cb0c886500f9b255f0e67c3', 0), +(1698, 11, 3706, 'effects.3706.nx4', '342bd479532ee509bd4558cae58b0e6f', 0), +(1699, 20, 3706, 'textures.3706.nx4', 'a8209cd96c3ce18e2e09188f46fe14cd', 0), +(1700, 18, 3706, 'projectiles.3706.nx4', 'aecc33193bcd9f442d8b74aa209f221d', 0), +(1701, 13, 3706, 'loc.3706.nx4', '6993c44ceaf9f0e2520174defbd5b588', 0), +(1702, 21, 3706, 'units.3706.nx4', '848507e9f8b27d1121dc3d96a9bcce26', 0), +(1703, 1, 3706, 'ForgedAlliance.3706.exe', 'c20b922a785cf5876c39b7696a16f162', 0); + +INSERT INTO `updates_fafdevelop` (`id`, `filename`, `path`) VALUES +(1, 'ForgedAlliance.exe', 'bin'), +(2, 'init_fafdevelop.lua', 'bin'), +(11, 'effects.nx5', 'gamedata'), +(12, 'env.nx5', 'gamedata'), +(13, 'loc.nx5', 'gamedata'), +(14, 'lua.nx5', 'gamedata'), +(15, 'meshes.nx5', 'gamedata'), +(17, 'modules.nx5', 'gamedata'), +(18, 'projectiles.nx5', 'gamedata'), +(19, 'schook.nx5', 'gamedata'), +(20, 'textures.nx5', 'gamedata'), +(21, 'units.nx5', 'gamedata'), +(22, 'etc.nx5', 'gamedata'); + +INSERT INTO `updates_fafdevelop_files` (`id`, `fileId`, `version`, `name`, `md5`, `obselete`) VALUES +(1, 2, 3652, 'init_fafdevelop_3658.lua', 'b701f97a451698c2caaef06dbdf72fe4', 0), +(2, 11, 3652, 'effects_0.3652.nxt', '31d289288784211fbf57fb639ce88a64', 0), +(3, 12, 3652, 'env_0.3652.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(4, 13, 3652, 'loc_0.3652.nxt', '97464f661ea84600543bbde05d12dd73', 0), +(5, 14, 3652, 'lua_0.3652.nxt', '7642fea8dfc908b490ec60db67ece06c', 0), +(6, 15, 3652, 'meshes_0.3652.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(7, 17, 3652, 'modules_0.3652.nxt', '6a81c76137fe7fe6579c55aabf03dbb7', 0), +(8, 18, 3652, 'projectiles_0.3652.nxt', 'ecbff75d5ee54dbea7baffec71a214b4', 0), +(9, 19, 3652, 'schook_0.3652.nxt', 'd70eb26a7f9eeddc28df711b839156b0', 0), +(10, 20, 3652, 'textures_0.3652.nxt', '1e0dd68c463f300234423a2346e5a979', 0), +(11, 21, 3652, 'units_0.3652.nxt', '0e91d45f764d27c7924a09384a47e60a', 0), +(12, 22, 3652, 'etc_0.3652.nxt', 'cb38d0bc92e4d01b8afce879bd3ab669', 0), +(24, 11, 3653, 'effects_0.3653.nxt', '7823e173c153ce3fc2977c68c54f1a3c', 0), +(25, 12, 3653, 'env_0.3653.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(26, 13, 3653, 'loc_0.3653.nxt', '85b23808ab6478b2fd303da8248bf738', 0), +(27, 14, 3653, 'lua_0.3653.nxt', 'f72663f46e1b15ec45314221a82b4d77', 0), +(28, 15, 3653, 'meshes_0.3653.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(29, 17, 3653, 'modules_0.3653.nxt', 'e20fec02e2251b5085cbe4096b7769a9', 0), +(30, 18, 3653, 'projectiles_0.3653.nxt', 'c1a7b415a3f5477470150e91580ccf19', 0), +(31, 19, 3653, 'schook_0.3653.nxt', 'fc661665d45af0976f5a70de863046a2', 0), +(32, 20, 3653, 'textures_0.3653.nxt', 'b72caa7d4e76ff71471badf55b8ee800', 0), +(33, 21, 3653, 'units_0.3653.nxt', '1255955a81ee7d73862ed6a54fb0d42f', 0), +(34, 22, 3653, 'etc_0.3653.nxt', '3277eb482e12fe0231fe692f356d935b', 0), +(35, 11, 3654, 'effects_0.3654.nxt', '7823e173c153ce3fc2977c68c54f1a3c', 0), +(36, 12, 3654, 'env_0.3654.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(37, 13, 3654, 'loc_0.3654.nxt', '6e9f6a5e148e94a8a292ed58d13f90d4', 0), +(38, 14, 3654, 'lua_0.3654.nxt', 'aaae2e5185b8ccb555296cb36f5af460', 0), +(39, 15, 3654, 'meshes_0.3654.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(40, 17, 3654, 'modules_0.3654.nxt', 'e20fec02e2251b5085cbe4096b7769a9', 0), +(41, 18, 3654, 'projectiles_0.3654.nxt', 'c1a7b415a3f5477470150e91580ccf19', 0), +(42, 19, 3654, 'schook_0.3654.nxt', 'fc661665d45af0976f5a70de863046a2', 0), +(43, 20, 3654, 'textures_0.3654.nxt', 'b72caa7d4e76ff71471badf55b8ee800', 0), +(44, 21, 3654, 'units_0.3654.nxt', '45e8a56d3d8f5750611961459f191c7a', 0), +(45, 22, 3654, 'etc_0.3654.nxt', '3277eb482e12fe0231fe692f356d935b', 0), +(46, 11, 3655, 'effects_0.3655.nxt', '7823e173c153ce3fc2977c68c54f1a3c', 0), +(47, 12, 3655, 'env_0.3655.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(48, 13, 3655, 'loc_0.3655.nxt', '6e9f6a5e148e94a8a292ed58d13f90d4', 0), +(49, 14, 3655, 'lua_0.3655.nxt', '5b34a8b361448cd3f515c96c4faca15b', 0), +(50, 15, 3655, 'meshes_0.3655.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(51, 17, 3655, 'modules_0.3655.nxt', 'e20fec02e2251b5085cbe4096b7769a9', 0), +(52, 18, 3655, 'projectiles_0.3655.nxt', 'c1a7b415a3f5477470150e91580ccf19', 0), +(53, 19, 3655, 'schook_0.3655.nxt', '122e008515f6a33e2d7936650373423e', 0), +(54, 20, 3655, 'textures_0.3655.nxt', 'ad4c595b8c0a0fb8b0e1e78ccf07c192', 0), +(55, 21, 3655, 'units_0.3655.nxt', '6fc20c5699bef0d18691fdf62cbb09d3', 0), +(56, 22, 3655, 'etc_0.3655.nxt', '3514201b87653551cf5db696556ec475', 0), +(57, 11, 3656, 'effects_0.3656.nxt', '7823e173c153ce3fc2977c68c54f1a3c', 0), +(58, 12, 3656, 'env_0.3656.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(59, 13, 3656, 'loc_0.3656.nxt', '670934cd424bc14e85c389c73e9f4175', 0), +(60, 14, 3656, 'lua_0.3656.nxt', '6dd341f362de25c336eaf963db1e2cad', 0), +(61, 15, 3656, 'meshes_0.3656.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(62, 17, 3656, 'modules_0.3656.nxt', 'e20fec02e2251b5085cbe4096b7769a9', 0), +(63, 18, 3656, 'projectiles_0.3656.nxt', 'c1a7b415a3f5477470150e91580ccf19', 0), +(64, 19, 3656, 'schook_0.3656.nxt', '122e008515f6a33e2d7936650373423e', 0), +(65, 20, 3656, 'textures_0.3656.nxt', 'ad4c595b8c0a0fb8b0e1e78ccf07c192', 0), +(66, 21, 3656, 'units_0.3656.nxt', 'a6413d206fc9db539bbe543adfc70b0f', 0), +(67, 22, 3656, 'etc_0.3656.nxt', '3514201b87653551cf5db696556ec475', 0), +(68, 11, 3657, 'effects_0.3657.nxt', '7823e173c153ce3fc2977c68c54f1a3c', 0), +(69, 12, 3657, 'env_0.3657.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(70, 13, 3657, 'loc_0.3657.nxt', '670934cd424bc14e85c389c73e9f4175', 0), +(71, 14, 3657, 'lua_0.3657.nxt', '225aefd5d4baa4e50c758cb06b880bb1', 0), +(72, 15, 3657, 'meshes_0.3657.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(73, 17, 3657, 'modules_0.3657.nxt', 'e20fec02e2251b5085cbe4096b7769a9', 0), +(74, 18, 3657, 'projectiles_0.3657.nxt', 'c1a7b415a3f5477470150e91580ccf19', 0), +(75, 19, 3657, 'schook_0.3657.nxt', '122e008515f6a33e2d7936650373423e', 0), +(76, 20, 3657, 'textures_0.3657.nxt', 'ad4c595b8c0a0fb8b0e1e78ccf07c192', 0), +(77, 21, 3657, 'units_0.3657.nxt', 'a6413d206fc9db539bbe543adfc70b0f', 0), +(78, 22, 3657, 'etc_0.3657.nxt', '3514201b87653551cf5db696556ec475', 0), +(79, 11, 3658, 'effects_0.3658.nxt', '7823e173c153ce3fc2977c68c54f1a3c', 0), +(80, 12, 3658, 'env_0.3658.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(81, 13, 3658, 'loc_0.3658.nxt', '670934cd424bc14e85c389c73e9f4175', 0), +(82, 14, 3658, 'lua_0.3658.nxt', 'b0f6c1a40bbc41e398ee9340ea14dd1f', 0), +(83, 15, 3658, 'meshes_0.3658.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(84, 17, 3658, 'modules_0.3658.nxt', 'e20fec02e2251b5085cbe4096b7769a9', 0), +(85, 18, 3658, 'projectiles_0.3658.nxt', 'c1a7b415a3f5477470150e91580ccf19', 0), +(86, 19, 3658, 'schook_0.3658.nxt', '122e008515f6a33e2d7936650373423e', 0), +(87, 20, 3658, 'textures_0.3658.nxt', 'ad4c595b8c0a0fb8b0e1e78ccf07c192', 0), +(88, 21, 3658, 'units_0.3658.nxt', 'a6413d206fc9db539bbe543adfc70b0f', 0), +(89, 22, 3658, 'etc_0.3658.nxt', '3514201b87653551cf5db696556ec475', 0), +(280, 11, 3659, 'effects_0.3659.nxt', '3758baad77531dd5323c766433412e91', 0), +(281, 12, 3659, 'env_0.3659.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(282, 13, 3659, 'loc_0.3659.nxt', 'caf6f9cf4909163ca37ecf1956874865', 0), +(283, 14, 3659, 'lua_0.3659.nxt', '5f557facb06c9006126b5be2a4881798', 0), +(284, 15, 3659, 'meshes_0.3659.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(285, 17, 3659, 'modules_0.3659.nxt', '89cc1c9c5f6a99a59966aaf30090bea6', 0), +(286, 18, 3659, 'projectiles_0.3659.nxt', '0ba261e8f444dfe7f4e12e748e5f159c', 0), +(287, 19, 3659, 'schook_0.3659.nxt', 'b9b6fe2c89d614d402a0335cf3e2fcba', 0), +(288, 20, 3659, 'textures_0.3659.nxt', 'f275298accf9552a6604ed62306add72', 0), +(289, 21, 3659, 'units_0.3659.nxt', '524b476ad5f64e36d9ddfe0a91a2c932', 0), +(290, 22, 3659, 'etc_0.3659.nxt', '3514201b87653551cf5db696556ec475', 0), +(810, 11, 3661, 'effects_0.3661.nxt', 'ce6d16a8ebd7f058a27f5ab6c0787530', 0), +(811, 12, 3661, 'env_0.3661.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(812, 13, 3661, 'loc_0.3661.nxt', 'ef8a035fc02a799be109741b9c411261', 0), +(813, 14, 3661, 'lua_0.3661.nxt', 'e6c58daa86a28ce1b620d8711db7bdd7', 0), +(814, 15, 3661, 'meshes_0.3661.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(815, 17, 3661, 'modules_0.3661.nxt', 'd39bb3e29bcc1fef41c0e47a705b6bb9', 0), +(816, 18, 3661, 'projectiles_0.3661.nxt', '28b6453e1e432985be201fd7bdde4250', 0), +(817, 19, 3661, 'schook_0.3661.nxt', '50304947f34ede05653867173dd78611', 0), +(818, 20, 3661, 'textures_0.3661.nxt', '76c69eb0dcc5963417539a2075feea85', 0), +(819, 21, 3661, 'units_0.3661.nxt', 'a5e8161b9deb4d7ab405da3ba527ba53', 0), +(820, 22, 3661, 'etc_0.3661.nxt', '3514201b87653551cf5db696556ec475', 0), +(821, 11, 3660, 'effects_0.3660.nxt', 'ce6d16a8ebd7f058a27f5ab6c0787530', 0), +(822, 12, 3660, 'env_0.3660.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(823, 13, 3660, 'loc_0.3660.nxt', '576fee631af11d7d4eddc5adcfd10dfa', 0), +(824, 14, 3660, 'lua_0.3660.nxt', '579315c17d249d32ea7d2ab5c208e4a1', 0), +(825, 15, 3660, 'meshes_0.3660.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(826, 17, 3660, 'modules_0.3660.nxt', 'd39bb3e29bcc1fef41c0e47a705b6bb9', 0), +(827, 18, 3660, 'projectiles_0.3660.nxt', '28b6453e1e432985be201fd7bdde4250', 0), +(828, 19, 3660, 'schook_0.3660.nxt', '99a7e3f33d013b550604d17103b2ba60', 0), +(829, 20, 3660, 'textures_0.3660.nxt', '5a2016efdeee75ff6197f659cfff1a7f', 0), +(830, 21, 3660, 'units_0.3660.nxt', '1ab896098a9b8206b5a15007ca7a5b98', 0), +(831, 22, 3660, 'etc_0.3660.nxt', '3514201b87653551cf5db696556ec475', 0), +(1026, 11, 3663, 'effects_0.3663.nxt', '4515b175e2b827131871ccbd72016c76', 0), +(1027, 12, 3663, 'env_0.3663.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(1028, 13, 3663, 'loc_0.3663.nxt', 'bd29abc6e7370990be1697ff4545c7df', 0), +(1029, 14, 3663, 'lua_0.3663.nxt', 'd4c5085046ed2d970ebb4145a3cf3366', 0), +(1030, 15, 3663, 'meshes_0.3663.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(1031, 17, 3663, 'modules_0.3663.nxt', '03b8425c87246120dfa92e394149ec45', 0), +(1032, 18, 3663, 'projectiles_0.3663.nxt', '28b6453e1e432985be201fd7bdde4250', 0), +(1033, 19, 3663, 'schook_0.3663.nxt', 'ce96f20d4bf19d8a231e97ac86e674fa', 0), +(1034, 20, 3663, 'textures_0.3663.nxt', '449c065dce0cb700b47bf50911d8b299', 0), +(1035, 21, 3663, 'units_0.3663.nxt', '2b852f5eda0d6a68903061869610885f', 0), +(1036, 22, 3663, 'etc_0.3663.nxt', '6dc10971d7ca2ada4a9f817a22370db0', 0), +(1037, 1, 3663, 'ForgedAlliance.3663.exe', '647693d2c680ff5177741aeeebbe9b5b', 0), +(1050, 11, 3664, 'effects_0.3664.nxt', '4515b175e2b827131871ccbd72016c76', 0), +(1051, 12, 3664, 'env_0.3664.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(1052, 13, 3664, 'loc_0.3664.nxt', 'bd29abc6e7370990be1697ff4545c7df', 0), +(1053, 14, 3664, 'lua_0.3664.nxt', 'd4c5085046ed2d970ebb4145a3cf3366', 0), +(1054, 15, 3664, 'meshes_0.3664.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(1055, 17, 3664, 'modules_0.3664.nxt', '03b8425c87246120dfa92e394149ec45', 0), +(1056, 18, 3664, 'projectiles_0.3664.nxt', '28b6453e1e432985be201fd7bdde4250', 0), +(1057, 19, 3664, 'schook_0.3664.nxt', 'ce96f20d4bf19d8a231e97ac86e674fa', 0), +(1058, 20, 3664, 'textures_0.3664.nxt', '449c065dce0cb700b47bf50911d8b299', 0), +(1059, 21, 3664, 'units_0.3664.nxt', '2b852f5eda0d6a68903061869610885f', 0), +(1060, 22, 3664, 'etc_0.3664.nxt', '6dc10971d7ca2ada4a9f817a22370db0', 0), +(1061, 1, 3664, 'ForgedAlliance.3664.exe', '37d717431bcd8549cebb74541ddb64fa', 0), +(1090, 11, 3667, 'effects_0.3667.nxt', '4515b175e2b827131871ccbd72016c76', 0), +(1091, 12, 3667, 'env_0.3667.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(1092, 13, 3667, 'loc_0.3667.nxt', 'bd29abc6e7370990be1697ff4545c7df', 0), +(1093, 14, 3667, 'lua_0.3667.nxt', 'cd78a834e1293b9ddd90522b042850dd', 0), +(1094, 15, 3667, 'meshes_0.3667.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(1095, 17, 3667, 'modules_0.3667.nxt', 'ef0d9fba50c0adaa1e4615c5fcf3c309', 0), +(1096, 18, 3667, 'projectiles_0.3667.nxt', '28b6453e1e432985be201fd7bdde4250', 0), +(1097, 19, 3667, 'schook_0.3667.nxt', 'ce96f20d4bf19d8a231e97ac86e674fa', 0), +(1098, 20, 3667, 'textures_0.3667.nxt', '449c065dce0cb700b47bf50911d8b299', 0), +(1099, 21, 3667, 'units_0.3667.nxt', '2b852f5eda0d6a68903061869610885f', 0), +(1100, 22, 3667, 'etc_0.3667.nxt', '0903a5a32cb70c564349dcfe84fba9a7', 0), +(1101, 1, 3667, 'ForgedAlliance.3667.exe', 'de8492bf49b6a980f5745db6892e3884', 0), +(1102, 11, 3668, 'effects_0.3668.nxt', '4515b175e2b827131871ccbd72016c76', 0), +(1103, 12, 3668, 'env_0.3668.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(1104, 13, 3668, 'loc_0.3668.nxt', 'bd29abc6e7370990be1697ff4545c7df', 0), +(1105, 14, 3668, 'lua_0.3668.nxt', 'cd78a834e1293b9ddd90522b042850dd', 0), +(1106, 15, 3668, 'meshes_0.3668.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(1107, 17, 3668, 'modules_0.3668.nxt', 'ef0d9fba50c0adaa1e4615c5fcf3c309', 0), +(1108, 18, 3668, 'projectiles_0.3668.nxt', '28b6453e1e432985be201fd7bdde4250', 0), +(1109, 19, 3668, 'schook_0.3668.nxt', 'ce96f20d4bf19d8a231e97ac86e674fa', 0), +(1110, 20, 3668, 'textures_0.3668.nxt', '449c065dce0cb700b47bf50911d8b299', 0), +(1111, 21, 3668, 'units_0.3668.nxt', '2b852f5eda0d6a68903061869610885f', 0), +(1112, 22, 3668, 'etc_0.3668.nxt', '0903a5a32cb70c564349dcfe84fba9a7', 0), +(1113, 1, 3668, 'ForgedAlliance.3668.exe', '43bf2699dfe2150b0215489e536b353e', 0), +(1122, 11, 3669, 'effects_0.3669.nxt', '4515b175e2b827131871ccbd72016c76', 0), +(1123, 12, 3669, 'env_0.3669.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(1124, 13, 3669, 'loc_0.3669.nxt', 'bd29abc6e7370990be1697ff4545c7df', 0), +(1125, 14, 3669, 'lua_0.3669.nxt', 'c2e23358be9c3cd71521cfe752fb6171', 0), +(1126, 15, 3669, 'meshes_0.3669.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(1127, 17, 3669, 'modules_0.3669.nxt', 'ef0d9fba50c0adaa1e4615c5fcf3c309', 0), +(1128, 18, 3669, 'projectiles_0.3669.nxt', '28b6453e1e432985be201fd7bdde4250', 0), +(1129, 19, 3669, 'schook_0.3669.nxt', 'ce96f20d4bf19d8a231e97ac86e674fa', 0), +(1130, 20, 3669, 'textures_0.3669.nxt', '449c065dce0cb700b47bf50911d8b299', 0), +(1131, 21, 3669, 'units_0.3669.nxt', '2b852f5eda0d6a68903061869610885f', 0), +(1132, 22, 3669, 'etc_0.3669.nxt', '0903a5a32cb70c564349dcfe84fba9a7', 0), +(1133, 1, 3669, 'ForgedAlliance.3669.exe', '67ffb1f83164df80153d7ac52ba373fb', 0), +(1158, 11, 3670, 'effects_0.3670.nxt', '4515b175e2b827131871ccbd72016c76', 0), +(1159, 12, 3670, 'env_0.3670.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(1160, 13, 3670, 'loc_0.3670.nxt', 'f6fe4ab343455c24248d67ead54dee33', 0), +(1161, 14, 3670, 'lua_0.3670.nxt', 'cc6c773bbce8e4e43e3ea8a3cb752a79', 0), +(1162, 15, 3670, 'meshes_0.3670.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(1163, 17, 3670, 'modules_0.3670.nxt', 'ef0d9fba50c0adaa1e4615c5fcf3c309', 0), +(1164, 18, 3670, 'projectiles_0.3670.nxt', 'fa77241c15b7508f853d8df7ea1702b7', 0), +(1165, 19, 3670, 'schook_0.3670.nxt', 'ce96f20d4bf19d8a231e97ac86e674fa', 0), +(1166, 20, 3670, 'textures_0.3670.nxt', '449c065dce0cb700b47bf50911d8b299', 0), +(1167, 21, 3670, 'units_0.3670.nxt', '58fbdedc6ed13a50ee59c3ba1469a697', 0), +(1168, 22, 3670, 'etc_0.3670.nxt', '7641a54879197b9bf372e863e20a8d19', 0), +(1169, 1, 3670, 'ForgedAlliance.3670.exe', '653dabf8e7b1acc87ea0e719436392c1', 0), +(1182, 11, 3671, 'effects_0.3671.nxt', '4515b175e2b827131871ccbd72016c76', 0), +(1183, 12, 3671, 'env_0.3671.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(1184, 13, 3671, 'loc_0.3671.nxt', 'bd29abc6e7370990be1697ff4545c7df', 0), +(1185, 14, 3671, 'lua_0.3671.nxt', '0590693f1d78d4c23daf4ea177dd8354', 0), +(1186, 15, 3671, 'meshes_0.3671.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(1187, 17, 3671, 'modules_0.3671.nxt', 'ef0d9fba50c0adaa1e4615c5fcf3c309', 0), +(1188, 18, 3671, 'projectiles_0.3671.nxt', '28b6453e1e432985be201fd7bdde4250', 0), +(1189, 19, 3671, 'schook_0.3671.nxt', 'ce96f20d4bf19d8a231e97ac86e674fa', 0), +(1190, 20, 3671, 'textures_0.3671.nxt', '449c065dce0cb700b47bf50911d8b299', 0), +(1191, 21, 3671, 'units_0.3671.nxt', '2b852f5eda0d6a68903061869610885f', 0), +(1192, 22, 3671, 'etc_0.3671.nxt', '0903a5a32cb70c564349dcfe84fba9a7', 0), +(1193, 1, 3671, 'ForgedAlliance.3671.exe', '073e3e613fe7df2826223c2ba5aa0f50', 0), +(1230, 11, 3672, 'effects_0.3672.nxt', '4515b175e2b827131871ccbd72016c76', 0), +(1231, 12, 3672, 'env_0.3672.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(1232, 13, 3672, 'loc_0.3672.nxt', 'f6fe4ab343455c24248d67ead54dee33', 0), +(1233, 14, 3672, 'lua_0.3672.nxt', '6915e88bf893fe3b7df42310809019cb', 0), +(1234, 15, 3672, 'meshes_0.3672.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(1235, 17, 3672, 'modules_0.3672.nxt', '4c91eadddbf40122f8cd09283fdc1683', 0), +(1236, 18, 3672, 'projectiles_0.3672.nxt', '67f0187848d02e26f771339dc9cbf2d4', 0), +(1237, 19, 3672, 'schook_0.3672.nxt', 'ce96f20d4bf19d8a231e97ac86e674fa', 0), +(1238, 20, 3672, 'textures_0.3672.nxt', '449c065dce0cb700b47bf50911d8b299', 0), +(1239, 21, 3672, 'units_0.3672.nxt', '9c650108c589ff7124279af231a1aa87', 0), +(1240, 22, 3672, 'etc_0.3672.nxt', '2bf5a88f0cd370dcc037a96627ecc81e', 0), +(1241, 1, 3672, 'ForgedAlliance.3672.exe', '6f60f0fae2ffa287c623d90743aa4ab4', 0), +(1242, 11, 3674, 'effects_0.3674.nxt', '4515b175e2b827131871ccbd72016c76', 0), +(1243, 12, 3674, 'env_0.3674.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(1244, 13, 3674, 'loc_0.3674.nxt', 'f6fe4ab343455c24248d67ead54dee33', 0), +(1245, 14, 3674, 'lua_0.3674.nxt', 'ff3f0dd4ea20d0f11f4c4370317eef33', 0), +(1246, 15, 3674, 'meshes_0.3674.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(1247, 17, 3674, 'modules_0.3674.nxt', '4c91eadddbf40122f8cd09283fdc1683', 0), +(1248, 18, 3674, 'projectiles_0.3674.nxt', '67f0187848d02e26f771339dc9cbf2d4', 0), +(1249, 19, 3674, 'schook_0.3674.nxt', 'ce96f20d4bf19d8a231e97ac86e674fa', 0), +(1250, 20, 3674, 'textures_0.3674.nxt', '449c065dce0cb700b47bf50911d8b299', 0), +(1251, 21, 3674, 'units_0.3674.nxt', '6645f72a57421d521bd80211d8fc79c9', 0), +(1252, 22, 3674, 'etc_0.3674.nxt', '2bf5a88f0cd370dcc037a96627ecc81e', 0), +(1253, 1, 3674, 'ForgedAlliance.3674.exe', '5f728e146a3813500dbf17fda88f1557', 0), +(1302, 11, 3675, 'effects_0.3675.nxt', '4515b175e2b827131871ccbd72016c76', 0), +(1303, 12, 3675, 'env_0.3675.nxt', '32a50729cb5155ec679771f38a151d29', 0), +(1304, 13, 3675, 'loc_0.3675.nxt', '4162370deb8d92424c43d844ebc59865', 0), +(1305, 14, 3675, 'lua_0.3675.nxt', '0d19d333b290b5ba130a13cc421e5228', 0), +(1306, 15, 3675, 'meshes_0.3675.nxt', '200972ee316ed09371dc9f357505bd66', 0), +(1307, 17, 3675, 'modules_0.3675.nxt', '845ac769d62457e7c29f478cb954041a', 0), +(1308, 18, 3675, 'projectiles_0.3675.nxt', '2381191f3caa384164ed457ac51b5a32', 0), +(1309, 19, 3675, 'schook_0.3675.nxt', 'ce96f20d4bf19d8a231e97ac86e674fa', 0), +(1310, 20, 3675, 'textures_0.3675.nxt', '68e175be351588751a33b88c3d0f533d', 0), +(1311, 21, 3675, 'units_0.3675.nxt', '112f0e8c019d9c2873b5676ee4e1a691', 0), +(1312, 22, 3675, 'etc_0.3675.nxt', '2bf5a88f0cd370dcc037a96627ecc81e', 0), +(1313, 1, 3675, 'ForgedAlliance.3675.exe', '54ad49b7838abfc1d498783614a0176a', 0), +(1857, 22, 3676, 'etc.3676.nxt', 'd144f2cb8e33d2999e5e170c13610bf1', 0), +(1858, 12, 3676, 'env.3676.nxt', '12a3bdf9584b8c1bb0fe27a360402139', 0), +(1859, 11, 3676, 'effects.3676.nxt', 'd07e0b2f9eee408dd0f3755ab20a9b0a', 0), +(1860, 21, 3676, 'units.3676.nxt', 'f53622ec56e720f3e90c50499d0ff3c9', 0), +(1861, 15, 3676, 'meshes.3676.nxt', '79ee53ca01b0bc8c53681b7b07340c50', 0), +(1862, 18, 3676, 'projectiles.3676.nxt', 'f6df327ad9cfa9fa0b7f9864d737701d', 0), +(1863, 19, 3676, 'schook.3676.nxt', '5f612cff52676380d3c0eab16561248c', 0), +(1864, 14, 3676, 'lua.3676.nxt', 'bbe52e8b6b742d0f2b00c88b1df70203', 0), +(1865, 13, 3676, 'loc.3676.nxt', '1a004a252e69fb7f5bf01f953693c81b', 0), +(1866, 20, 3676, 'textures.3676.nxt', 'c6700711c7fdcfc8d1ed96ae5df977b1', 0), +(1867, 1, 3676, 'ForgedAlliance.3676.exe', 'cb9d3ef03be5800ce761c9e4914d9b86', 0), +(1979, 22, 3677, 'etc.3677.nxt', '2e4d342edef0d06d607bd2f1c0bba91d', 0), +(1980, 12, 3677, 'env.3677.nxt', '5ec78d878741926662a0f64169884acb', 0), +(1981, 11, 3677, 'effects.3677.nxt', 'f0628838c57c7284b87354a5a6e32f27', 0), +(1982, 21, 3677, 'units.3677.nxt', 'd07495b009151c00216aa468ba775a25', 0), +(1983, 15, 3677, 'meshes.3677.nxt', '06e52e245b29653237782b3768165f00', 0), +(1984, 18, 3677, 'projectiles.3677.nxt', '61473752278829fc95647f973c706d04', 0), +(1985, 19, 3677, 'schook.3677.nxt', '767b2025a66cecfa11f89760e6a260ac', 0), +(1986, 14, 3677, 'lua.3677.nxt', '5d5cc02199e6cfba6531bc38aa87347d', 0), +(1987, 13, 3677, 'loc.3677.nxt', 'da07496ec292c67ea6220160adffb392', 0), +(1988, 20, 3677, 'textures.3677.nxt', '1a6e17e2f0aa2dc0a4a8e415200a2fbb', 0), +(1989, 1, 3677, 'ForgedAlliance.3677.exe', '2b703fefa308f13cea32482bf8b464d6', 0), +(2001, 22, 3680, 'etc.3680.nxt', 'b1f2da0273dbfb2d9dec6aecace4f752', 0), +(2002, 12, 3680, 'env.3680.nxt', 'bcf999bd23d5c145107fa21e660ed659', 0), +(2003, 11, 3680, 'effects.3680.nxt', '0e588c32d628a5ea9f860f19b92a9b70', 0), +(2004, 21, 3680, 'units.3680.nxt', '052e15428aac4015dbfc3fc645dfad22', 0), +(2005, 15, 3680, 'meshes.3680.nxt', '276451be231bae8c296803e716f7ef84', 0), +(2006, 18, 3680, 'projectiles.3680.nxt', 'bf0ff098bc713431f33e2507388e911d', 0), +(2007, 19, 3680, 'schook.3680.nxt', '10e31edb645ac05196b43dbad8625757', 0), +(2008, 14, 3680, 'lua.3680.nxt', '20c4b451d40a52dd97b192f9c52d2923', 0), +(2009, 13, 3680, 'loc.3680.nxt', 'feeaaec787616864b5162d36d74d9185', 0), +(2010, 20, 3680, 'textures.3680.nxt', 'c265f40ddb36d3146ba3fbd79951fe52', 0), +(2011, 1, 3680, 'ForgedAlliance.3680.exe', '26fc5a5a8e56c86684c989d3795fc8b8', 0), +(2122, 22, 3681, 'etc.3681.nxt', 'e68bb21d55b69448eaa7c49757932b3e', 0), +(2123, 12, 3681, 'env.3681.nxt', '19396af2616c366ac44c5400c04566a6', 0), +(2124, 11, 3681, 'effects.3681.nxt', '91269553a81c786ceae9b042de48daab', 0), +(2125, 21, 3681, 'units.3681.nxt', 'e6cd004d96a9ce6798d21ac6c8d5dc79', 0), +(2126, 15, 3681, 'meshes.3681.nxt', '62e0343eb7c329175b393133f901fbad', 0), +(2127, 18, 3681, 'projectiles.3681.nxt', '11d9a6cdf5c699b9964d1e0fbc89226b', 0), +(2128, 19, 3681, 'schook.3681.nxt', 'f3f0e7f5b3eb8cbdc46c9423a3b5e8e7', 0), +(2129, 14, 3681, 'lua.3681.nxt', 'e3fc51a70b7a4e71f759ca16d6bc13a8', 0), +(2130, 13, 3681, 'loc.3681.nxt', '76e95d68bdc62593fedafaf8385c7243', 0), +(2131, 20, 3681, 'textures.3681.nxt', '4a4f6d5075c6997b56fedc0edd4df824', 0), +(2132, 1, 3681, 'ForgedAlliance.3681.exe', '0e9d74f715ac757e3b35c6d14d5904a9', 0), +(2221, 22, 3682, 'etc.3682.nx5', 'e16647f042a4049a00973d34e8e4f898', 0), +(2222, 12, 3682, 'env.3682.nx5', '01a48db1a5fe44a14755e19ed1dc8a2d', 0), +(2223, 11, 3682, 'effects.3682.nx5', 'ca693fb1beeef2e328c3c3b04b38f0d3', 0), +(2224, 21, 3682, 'units.3682.nx5', '645a6dfe52aa1adefd66e46be893ac45', 0), +(2225, 15, 3682, 'meshes.3682.nx5', 'ac1c7593891d2da31298394726c7dfeb', 0), +(2226, 18, 3682, 'projectiles.3682.nx5', '048ee840fd2f5d277d908934aefe009f', 0), +(2227, 19, 3682, 'schook.3682.nx5', 'bd219697990f8538aeff783574319ded', 0), +(2228, 14, 3682, 'lua.3682.nx5', '2fdda427cfd201cc522c02086a9f4f7c', 0), +(2229, 13, 3682, 'loc.3682.nx5', '4e18a100517e1433d9cf5f9817f36603', 0), +(2230, 20, 3682, 'textures.3682.nx5', '8c60e0b214952e8f78693ed24945596b', 0), +(2231, 1, 3682, 'ForgedAlliance.3682.exe', '334e86d0e0531e3539b1e3c8111e452c', 0), +(2562, 22, 3684, 'etc.3684.nx5', '66ce0ef65130ab5cb81e78abc3936c03', 0), +(2563, 12, 3684, 'env.3684.nx5', '4a62907c19ba991b13eac31526dc209f', 0), +(2564, 11, 3684, 'effects.3684.nx5', 'f99f83007f1ce6f6195b4c4963c1ff34', 0), +(2565, 21, 3684, 'units.3684.nx5', '0bfe7094e292bd591fac8d68708b959f', 0), +(2566, 15, 3684, 'meshes.3684.nx5', '249645a4524dd07a27ec6411bbe1403a', 0), +(2567, 18, 3684, 'projectiles.3684.nx5', 'edf422877f0814c15442ad579ae3b367', 0), +(2568, 19, 3684, 'schook.3684.nx5', 'ea0889a39384543c80ac3a249065c202', 0), +(2569, 14, 3684, 'lua.3684.nx5', 'c3edd3c545ada55f7c4b475dcfe26983', 0), +(2570, 13, 3684, 'loc.3684.nx5', 'ab659d90d242d18ffbbb9e072d374d84', 0), +(2571, 20, 3684, 'textures.3684.nx5', '6101d812e5e3a754f0520be130dafd5d', 0), +(2572, 1, 3684, 'ForgedAlliance.3684.exe', '0f9e085d44da00e0ce314c181cf087a8', 0), +(2672, 22, 3686, 'etc.3686.nx5', 'f19941aeece3e058ad241751574bc597', 0), +(2673, 12, 3686, 'env.3686.nx5', '631738db2a1b1294c86f6c8d17a47510', 0), +(2674, 11, 3686, 'effects.3686.nx5', 'b558676a9041948c7abd466a25d1de27', 0), +(2675, 21, 3686, 'units.3686.nx5', '42171f5c9e6db71dbb062845c7a1c60d', 0), +(2676, 1, 3686, 'ForgedAlliance.3686.exe', '4d939f813fcf987435669719e703ca89', 0), +(2721, 12, 3689, 'env.3689.nx5', '43b16f458187031120415d7d181d343e', 0), +(2722, 15, 3689, 'meshes.3689.nx5', 'a45675e23460ddc1fc7a8a3c074f8bb6', 0), +(2723, 19, 3689, 'schook.3689.nx5', '242e4b2e659e1efcaaba73beecf12d63', 0), +(2724, 14, 3689, 'lua.3689.nx5', '24b8cc6fe20b777e8d7ad5723734a145', 0), +(2725, 22, 3689, 'etc.3689.nx5', '5515f32cf94f4e7139bfbac4ed0bb136', 0), +(2726, 11, 3689, 'effects.3689.nx5', '5559e01e9b8d24fb94a0033f4ec8c819', 0), +(2727, 20, 3689, 'textures.3689.nx5', 'b8446b90c4bde7f92916c3a4fc6f55f4', 0), +(2728, 18, 3689, 'projectiles.3689.nx5', '69086d8ac43c01e44fb6bad7d6fadc4a', 0), +(2729, 13, 3689, 'loc.3689.nx5', '127b1f96afc715db6fd5e4fdcaa609ed', 0), +(2730, 21, 3689, 'units.3689.nx5', '18ed96f4e1e5e27855045015a3ce9136', 0), +(2731, 1, 3689, 'ForgedAlliance.3689.exe', '6ca19563c80509050f7693cd19d1526e', 0), +(2754, 12, 3692, 'env.3692.nx5', '28dd67ded9e108daff490b66b5640584', 0), +(2755, 15, 3692, 'meshes.3692.nx5', 'b3acbd07e654731b612656fa222f006b', 0), +(2756, 19, 3692, 'schook.3692.nx5', '8f71b1d1ab63b896313b634f3d677430', 0), +(2757, 14, 3692, 'lua.3692.nx5', '0342fe9bcbabd101f905c8280dbe0bf0', 0), +(2758, 22, 3692, 'etc.3692.nx5', '54629e2541127ec20621e241962bc2ad', 0), +(2759, 11, 3692, 'effects.3692.nx5', 'e59967fbaf767364d551e5e30fb5dda7', 0), +(2760, 20, 3692, 'textures.3692.nx5', '89aed3034894a0e1834de753633a75f7', 0), +(2761, 18, 3692, 'projectiles.3692.nx5', '0f94413ffecf3a9ce63aac56b0b4bcc8', 0), +(2762, 13, 3692, 'loc.3692.nx5', '6abfacf9e53c9b198d47574901568e4c', 0), +(2763, 21, 3692, 'units.3692.nx5', '37bc714b0fa72502fcb9b3c366fffb54', 0), +(2764, 1, 3692, 'ForgedAlliance.3692.exe', '9aa457e1e32dbbf3f188ecb08db4db3d', 0), +(2776, 12, 3693, 'env.3693.nx5', '5012ee4765b99c71c08a8e4c851d57d8', 0), +(2777, 15, 3693, 'meshes.3693.nx5', 'f5a6e6b897b9d66a8127ed8ad3019255', 0), +(2778, 19, 3693, 'schook.3693.nx5', 'b2a99e560214b6601a6d4931a519c155', 0), +(2779, 14, 3693, 'lua.3693.nx5', '92f2ad5ee57f0c5f18f16b06c45d02b6', 0), +(2780, 22, 3693, 'etc.3693.nx5', '0debd69000a4f48c7b1ed5cc6a950f7d', 0), +(2781, 11, 3693, 'effects.3693.nx5', 'f424e20adcf364c0a6cb54f8d253db9a', 0), +(2782, 20, 3693, 'textures.3693.nx5', '46ebc4eb5d0f8be805c743aff7f4e3fc', 0), +(2783, 18, 3693, 'projectiles.3693.nx5', '4b3264e5495a4749aeb1c7869a079b25', 0), +(2784, 13, 3693, 'loc.3693.nx5', '8fc9e43021614f61bd1e17eec0fb6f9c', 0), +(2785, 21, 3693, 'units.3693.nx5', '70c92a904913256d8557a26cc74a76b3', 0), +(2786, 1, 3693, 'ForgedAlliance.3693.exe', '653d9b3d68bedc34c25969a6c021ed26', 0), +(2809, 12, 3695, 'env.3695.nx5', '97b307ed18545a77c4aa5cec3e50c5fa', 0), +(2810, 15, 3695, 'meshes.3695.nx5', '847f642e0e60c687ba5465741d3ebcb4', 0), +(2811, 19, 3695, 'schook.3695.nx5', '57c1d22b81bbf4c59a85d695b7a61558', 0), +(2812, 14, 3695, 'lua.3695.nx5', 'bb03e905e5b647ad8c3636c427da2156', 0), +(2813, 22, 3695, 'etc.3695.nx5', '5fb5dc45ed91ea10f8e6374ec33c9a89', 0), +(2814, 11, 3695, 'effects.3695.nx5', 'cb2eacedd14d5e0e5df6999e18f99670', 0), +(2815, 20, 3695, 'textures.3695.nx5', '7e1b19599486602f49274abb0ed9d4a9', 0), +(2816, 18, 3695, 'projectiles.3695.nx5', '8fb19a7b1fbab53a460b8bd150ea2b89', 0), +(2817, 13, 3695, 'loc.3695.nx5', 'b82f4cc0e177e8d894fbffe579b9aedd', 0), +(2818, 21, 3695, 'units.3695.nx5', 'e297f043b9f3e802d8cbf956548d29f2', 0), +(2819, 1, 3695, 'ForgedAlliance.3695.exe', '199c53981051617a87ed15a787961572', 0), +(2930, 12, 3696, 'env.3696.nx5', '52edd5da539b9f57245775c107c22b51', 0), +(2931, 15, 3696, 'meshes.3696.nx5', '801676f280793f9d9146b3de516756dc', 0), +(2932, 19, 3696, 'schook.3696.nx5', '0a69570c165f64129a5e63f3a95606a6', 0), +(2933, 14, 3696, 'lua.3696.nx5', '287d294d54e0e9181ac0e42c7d23bc8c', 0), +(2934, 22, 3696, 'etc.3696.nx5', '935db5f25054cded455788797649fbfa', 0), +(2935, 11, 3696, 'effects.3696.nx5', '30c75a62927676217e3b49780646f8d5', 0), +(2936, 20, 3696, 'textures.3696.nx5', 'c169378b8a798c906b18cdfe9ea9998e', 0), +(2937, 18, 3696, 'projectiles.3696.nx5', 'e95b9ab0c571bdbe90b252b604ef46cb', 0), +(2938, 13, 3696, 'loc.3696.nx5', 'eafc55938746388c1cebf0e0b2d79ec8', 0), +(2939, 21, 3696, 'units.3696.nx5', 'de611fc390578c64b0889b56e278f681', 0), +(2940, 1, 3696, 'ForgedAlliance.3696.exe', '3050ad5780eb9ad1711859a9f25a67c3', 0), +(2975, 12, 3697, 'env.3697.nx5', 'eb2e564492dc30e4648d79545419f54f', 0), +(2976, 15, 3697, 'meshes.3697.nx5', '08c2bfdc0e5b7eeecec8b547b570bd99', 0), +(2977, 19, 3697, 'schook.3697.nx5', '78a9dd27f06d62608c1ef9c46b43c9e9', 0), +(2978, 14, 3697, 'lua.3697.nx5', 'c2ac724c53eb14f2962a24fe53651cb9', 0), +(2979, 22, 3697, 'etc.3697.nx5', 'b9a1233df750fd32ad31838dc417a04e', 0), +(2980, 11, 3697, 'effects.3697.nx5', 'b38fcb37a0f3c41aa51a9f873a5049c6', 0), +(2981, 20, 3697, 'textures.3697.nx5', '0ff641bccdce28aaa78f51a286b5f495', 0), +(2982, 18, 3697, 'projectiles.3697.nx5', '3eb421ec6b8ad51926bfe4e00e0ab21c', 0), +(2983, 13, 3697, 'loc.3697.nx5', '810318da25c4d283ab40ee0a2124b226', 0), +(2984, 21, 3697, 'units.3697.nx5', '52f46a380e136275cede302bcb407128', 0), +(2985, 1, 3697, 'ForgedAlliance.3697.exe', '83bd429f0f2f119a4539faa48f67c71c', 0), +(3250, 12, 3698, 'env.3698.nx5', 'e9a409a30bf100afb701c18719afe789', 0), +(3251, 15, 3698, 'meshes.3698.nx5', 'a20a9d513e56e8e2620373b047365469', 0), +(3252, 19, 3698, 'schook.3698.nx5', '5e3aabf39c41ae625261c01fed9b445e', 0), +(3253, 14, 3698, 'lua.3698.nx5', 'd0911dcecb0d60eb72deff9aed137eaa', 0), +(3254, 22, 3698, 'etc.3698.nx5', '90490c5f4c207dbb7f6d8f6912940a4d', 0), +(3255, 11, 3698, 'effects.3698.nx5', 'b5a7062c1ff354a99b4f36093f6dadc4', 0), +(3256, 20, 3698, 'textures.3698.nx5', 'a8ef3bbef23a04d9a8496c0f892bac9a', 0), +(3257, 18, 3698, 'projectiles.3698.nx5', 'dcf9f558444a6706a4e50f4362706659', 0), +(3258, 13, 3698, 'loc.3698.nx5', '70ff10e21bdb0d0f9b524b3c9bab1dd6', 0), +(3259, 21, 3698, 'units.3698.nx5', '7d0aa197f5df098b899a6eebb7945fae', 0), +(3260, 1, 3698, 'ForgedAlliance.3698.exe', 'c0f936b1c9675f57b688ad00a5c486de', 0), +(3272, 12, 3699, 'env.3699.nx5', 'e732ce847e35e5cd9e420b4d9ff63fc1', 0), +(3273, 15, 3699, 'meshes.3699.nx5', 'c3a91ea2e7690d5436e5f09cbc09512a', 0), +(3274, 19, 3699, 'schook.3699.nx5', '5c7295576d95900e40e85519fdea54e7', 0), +(3275, 14, 3699, 'lua.3699.nx5', '9013cb049fa677994448670cd17081d7', 0), +(3276, 22, 3699, 'etc.3699.nx5', 'e653ef1ce9182ce1b48b91b1db2f1f6c', 0), +(3277, 11, 3699, 'effects.3699.nx5', 'f88f432e31e55cf2c6da4ac210e29a7a', 0), +(3278, 20, 3699, 'textures.3699.nx5', 'cc1baf98bf2565c02f6bcae268ad4cf5', 0), +(3279, 18, 3699, 'projectiles.3699.nx5', '17f490bc88a2f8e393491c1d66df5b9e', 0), +(3280, 13, 3699, 'loc.3699.nx5', '412b39d81a8966c0ad32514b0bd6419f', 0), +(3281, 21, 3699, 'units.3699.nx5', '9e028cfb8262a594109eb2cb87450abc', 0), +(3282, 1, 3699, 'ForgedAlliance.3699.exe', 'a269c439b663bae1a3fb90bef6ad03a8', 0), +(3360, 12, 3700, 'env.3700.nx5', '5c51a96c0c9e724bfa353a1afa19d70a', 0), +(3361, 15, 3700, 'meshes.3700.nx5', '261fa982d651fa1aeae84723734d0c01', 0), +(3362, 19, 3700, 'schook.3700.nx5', '92d50557cf75054e4d1fb99278bb03a8', 0), +(3363, 14, 3700, 'lua.3700.nx5', '318d5939ddd4995ea2ecf4edab1ab5d3', 0), +(3364, 22, 3700, 'etc.3700.nx5', 'b407bbffb36e846e67bf79941381a3a1', 0), +(3365, 11, 3700, 'effects.3700.nx5', 'fc545dc1e75e288a1a1958c07992f3b6', 0), +(3366, 20, 3700, 'textures.3700.nx5', 'ad8c6508b2a20b188a1c303a53605702', 0), +(3367, 18, 3700, 'projectiles.3700.nx5', 'd7939dc69f4de576000a88ee32912c86', 0), +(3368, 13, 3700, 'loc.3700.nx5', '2552498024fb54697b0fc0042f9e6ccd', 0), +(3369, 21, 3700, 'units.3700.nx5', '88444f32bb11bf9b5f8dc744a6fdca22', 0), +(3370, 1, 3700, 'ForgedAlliance.3700.exe', 'e47d09b9b2ac5c1e1562b3393d47ceb6', 0), +(3569, 12, 3701, 'env.3701.nx5', '3a33074a804e5a7a631f8a6406ac8e54', 0), +(3570, 15, 3701, 'meshes.3701.nx5', '4c7c2e285b1827704f541c54965a5ee4', 0), +(3571, 19, 3701, 'schook.3701.nx5', '2054c4c7ec6129a7ffd0ba1c1908c887', 0), +(3572, 14, 3701, 'lua.3701.nx5', '88d58e295c4fec77a9e41ac8db9c8814', 0), +(3573, 22, 3701, 'etc.3701.nx5', 'de1ca89f8e73b518f8a438ecc6edeccd', 0), +(3574, 11, 3701, 'effects.3701.nx5', 'aab6977a716613b98eca849e50bbb081', 0), +(3575, 20, 3701, 'textures.3701.nx5', '7eaef838c78522d688f0f422b6b6a77c', 0), +(3576, 18, 3701, 'projectiles.3701.nx5', '4be08f51cf00bd56a9b9e74223f12c60', 0), +(3577, 13, 3701, 'loc.3701.nx5', 'c24e10e8af849ae5dfa4a85a85fbe46f', 0), +(3578, 21, 3701, 'units.3701.nx5', 'efb20720ef77ca7de09578e206de2623', 0), +(3579, 1, 3701, 'ForgedAlliance.3701.exe', 'f11fdb5c09a8d8999f90358560f5e627', 0), +(3690, 12, 3702, 'env.3702.nx5', '336dba49f9ab129bb9db7319038e9927', 0), +(3691, 15, 3702, 'meshes.3702.nx5', 'a62acb96c6fbdd313306f7860b2d3726', 0), +(3692, 19, 3702, 'schook.3702.nx5', '23060a3f11abae6dcab83a23eba187d3', 0), +(3693, 14, 3702, 'lua.3702.nx5', '03cb2004eb0e9421e3063ce9dcfdd7af', 0), +(3694, 22, 3702, 'etc.3702.nx5', 'bd6ef8fceb2e0b2c880fdb8bc3143cb8', 0), +(3695, 11, 3702, 'effects.3702.nx5', '3da4a4887633b6efbf6e46210bfd6fd4', 0), +(3696, 20, 3702, 'textures.3702.nx5', '138c40f5face903fb52d2977e444a122', 0), +(3697, 18, 3702, 'projectiles.3702.nx5', '55a943779ca3a73d8dc203e0fef87e9e', 0), +(3698, 13, 3702, 'loc.3702.nx5', '95a42c088d041f45a0a675a807c5b3be', 0), +(3699, 21, 3702, 'units.3702.nx5', '917bf58604eb0284c2f99521013b39cc', 0), +(3700, 1, 3702, 'ForgedAlliance.3702.exe', '819558d8cf924dce94c038882e6ed229', 0), +(3855, 12, 3703, 'env.3703.nx5', 'd32462eb04d059fdfd76ff969527f05c', 0), +(3856, 15, 3703, 'meshes.3703.nx5', 'cdcbbd1f612d2f1f20f3c1d09a04d288', 0), +(3857, 19, 3703, 'schook.3703.nx5', '518b50174270e15c2bbf721d0af20e3b', 0), +(3858, 14, 3703, 'lua.3703.nx5', '18ea0e3ec10e6651b5e64edfa34c4215', 0), +(3859, 22, 3703, 'etc.3703.nx5', '06d9f282bfda5189c68e26f50a615c1b', 0), +(3860, 11, 3703, 'effects.3703.nx5', '816e4222eef38f922d0efed815aa3161', 0), +(3861, 20, 3703, 'textures.3703.nx5', 'b2c29fc0dedf42d406fef99f1d42dcb5', 0), +(3862, 18, 3703, 'projectiles.3703.nx5', '475db0befe255d2362499b79ed2c7db5', 0), +(3863, 13, 3703, 'loc.3703.nx5', 'c3df5a3e916d2d19c8fd1d7e9aadc599', 0), +(3864, 21, 3703, 'units.3703.nx5', '2e5d0ecdf922240b190977ff7b956db2', 0), +(3865, 1, 3703, 'ForgedAlliance.3703.exe', '80d1355256229ab53ba6f24db1fd6a7f', 0), +(3965, 12, 3704, 'env.3704.nx5', '3e28c9290eef7a4e95d0d7aceeead656', 0), +(3966, 15, 3704, 'meshes.3704.nx5', 'de7f1c8ffed4979e4709e86850d469f0', 0), +(3967, 19, 3704, 'schook.3704.nx5', '799b598b81e4b33822e938d7c34d9110', 0), +(3968, 14, 3704, 'lua.3704.nx5', '4ae8ce5e9942a273f902543686df6a67', 0), +(3969, 22, 3704, 'etc.3704.nx5', '5123617450af795abd529d20f8be0f4d', 0), +(3970, 11, 3704, 'effects.3704.nx5', '612fb4cdc033ade7a8046fcaa4a26df6', 0), +(3971, 20, 3704, 'textures.3704.nx5', 'fa337107791ec1fb32f83519c83fa92b', 0), +(3972, 18, 3704, 'projectiles.3704.nx5', '8b79a1563a9ac7c55e656577b7569820', 0), +(3973, 13, 3704, 'loc.3704.nx5', '210450e633b373484ae17cdef4ec6d54', 0), +(3974, 21, 3704, 'units.3704.nx5', '61a7f6d8ed402f3be71c183b034fb31d', 0), +(3975, 1, 3704, 'ForgedAlliance.3704.exe', 'c3d48349ce61d9243046a1b1a4428912', 0), +(4097, 12, 3705, 'env.3705.nx5', '5ff9c2b8c3cc550ab2c0e5aea2422b11', 0), +(4098, 15, 3705, 'meshes.3705.nx5', 'c557257a69bf0aa932ddc01ea5457760', 0), +(4099, 19, 3705, 'schook.3705.nx5', '4cb441557a89d772245991bb97da48a1', 0), +(4100, 14, 3705, 'lua.3705.nx5', '80c12c6cbe15fd2f7119ad4e0dc1ceb5', 0), +(4101, 22, 3705, 'etc.3705.nx5', 'faeaa5094f600cec27cb28dfbb4ee210', 0), +(4102, 11, 3705, 'effects.3705.nx5', '7c46f280170c87a3b408436f6c4c3c1a', 0), +(4103, 20, 3705, 'textures.3705.nx5', '35a698d575b9a1c5036dee1f195ff6da', 0), +(4104, 18, 3705, 'projectiles.3705.nx5', '6d851bc2913031fdea04d912b8871b62', 0), +(4105, 13, 3705, 'loc.3705.nx5', '84d46f35a637e600687cee721d99c442', 0), +(4106, 21, 3705, 'units.3705.nx5', '8d8202323d66d062193aa7f66f718316', 0), +(4107, 1, 3705, 'ForgedAlliance.3705.exe', '573296042dc5eb6e13c4ba5f7b855539', 0), +(4119, 12, 3706, 'env.3706.nx5', 'b9a854b4c3d6d24fd6d537661746a3fa', 0), +(4120, 15, 3706, 'meshes.3706.nx5', '882b79404ab791b08713071c84316179', 0), +(4121, 19, 3706, 'schook.3706.nx5', 'fe10bf92f58db6656c3b403d2c3405c2', 0), +(4122, 14, 3706, 'lua.3706.nx5', 'f114a76320765837316dfad0edbdf8bd', 0), +(4123, 22, 3706, 'etc.3706.nx5', '201e60463bff73402d8b1016d2758cf9', 0), +(4124, 11, 3706, 'effects.3706.nx5', '2641a986264112dd6d4293e06a6146b5', 0), +(4125, 20, 3706, 'textures.3706.nx5', '783af5877d8b8a77cba25526f0f6466f', 0), +(4126, 18, 3706, 'projectiles.3706.nx5', '14a804000945dc430198970efef25fbb', 0), +(4127, 13, 3706, 'loc.3706.nx5', '96d67c3f7af0a9888e3d2dc227a4d170', 0), +(4128, 21, 3706, 'units.3706.nx5', '4759b0c48c072df42d21b8162c79af73', 0), +(4129, 1, 3706, 'ForgedAlliance.3706.exe', 'c20b922a785cf5876c39b7696a16f162', 0), +(4317, 12, 3707, 'env.3707.nx5', 'a261b4c97679aa69fdccc9ed2c1aaa97', 0), +(4318, 15, 3707, 'meshes.3707.nx5', 'eed1e3a85dc20296b5d29f2a51471728', 0), +(4319, 19, 3707, 'schook.3707.nx5', 'b8245efcd38211a8b04db584ab8ffa27', 0), +(4320, 14, 3707, 'lua.3707.nx5', 'e648b55afbc94cbc0cbf603354f7b28e', 0), +(4321, 22, 3707, 'etc.3707.nx5', '3bd928096ed69156143daa0c38fb8955', 0), +(4322, 11, 3707, 'effects.3707.nx5', 'f9e92b4535117907cc187ad255e6397f', 0), +(4323, 20, 3707, 'textures.3707.nx5', '748e6f92720ea5dac7a52b4ac05449d1', 0), +(4324, 18, 3707, 'projectiles.3707.nx5', 'a57e08debfaba57532f2695580c6f45f', 0), +(4325, 13, 3707, 'loc.3707.nx5', '9fbf48ac4f13432ddbf056b7a38a7edb', 0), +(4326, 21, 3707, 'units.3707.nx5', '30d14d3365f28777c53ae5b288c1fc2d', 0), +(4327, 1, 3707, 'ForgedAlliance.3707.exe', '79f0ea70625ab464d369721183e9fd29', 0); diff --git a/src/inttest/resources/sql/prepGameData.sql b/src/inttest/resources/sql/prepGameData.sql index 3d6dbee40..ea644532d 100644 --- a/src/inttest/resources/sql/prepGameData.sql +++ b/src/inttest/resources/sql/prepGameData.sql @@ -8,5 +8,7 @@ VALUES (25, 'coop', 'Coop', 'Multiplayer campaign games', 1, 'https://github.com/FAForever/fa-coop.git', 'master', 'cop', TRUE); +-- INSERT INTO `game_validity` (`id`, `message`) VALUES (0, 'Valid') ON DUPLICATE KEY UPDATE `id` = VALUES(id); + INSERT INTO game_stats (id, startTime, gameName, gameType, gameMod, `host`, mapId, validity) VALUES (1, NOW(), 'Test game', '0', 6, 1, 1, 0); diff --git a/src/main/java/com/faforever/api/config/FafApiProperties.java b/src/main/java/com/faforever/api/config/FafApiProperties.java index 1a9969406..222456320 100644 --- a/src/main/java/com/faforever/api/config/FafApiProperties.java +++ b/src/main/java/com/faforever/api/config/FafApiProperties.java @@ -157,6 +157,10 @@ public static class Deployment { private String repositoriesDirectory; private String filesDirectoryFormat = "updates_%s_files"; private String forgedAllianceExePath; + private String testingExeUploadKey; + private String allowedExeExtension = "exe"; + private String forgedAllianceBetaExePath; + private String forgedAllianceDevelopExePath; } @Data diff --git a/src/main/java/com/faforever/api/config/swagger/SwaggerConfig.java b/src/main/java/com/faforever/api/config/swagger/SwaggerConfig.java index 1b85572b1..28eed5c44 100644 --- a/src/main/java/com/faforever/api/config/swagger/SwaggerConfig.java +++ b/src/main/java/com/faforever/api/config/swagger/SwaggerConfig.java @@ -60,6 +60,7 @@ private Predicate paths() { regex("/users/.*"), regex("/mods/.*"), regex("/maps/.*"), + regex("/exe/.*"), regex("/leaderboards/.*"), regex("/voting/.*")); } diff --git a/src/main/java/com/faforever/api/deployment/ExeUploaderController.java b/src/main/java/com/faforever/api/deployment/ExeUploaderController.java new file mode 100644 index 000000000..bde2eef89 --- /dev/null +++ b/src/main/java/com/faforever/api/deployment/ExeUploaderController.java @@ -0,0 +1,59 @@ +package com.faforever.api.deployment; + +import com.faforever.api.config.FafApiProperties; +import com.faforever.api.error.ApiException; +import com.faforever.api.error.Error; +import com.faforever.api.error.ErrorCode; +import com.google.common.io.Files; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8_VALUE; + +@RestController +@RequestMapping(path = "/exe") +@Slf4j +public class ExeUploaderController { + private final FafApiProperties apiProperties; + private final ExeUploaderService exeUploaderService; + + public ExeUploaderController( + FafApiProperties apiProperties, + ExeUploaderService exeUploaderService + ) { + this.apiProperties = apiProperties; + this.exeUploaderService = exeUploaderService; + } + + @ApiOperation("Upload an exe file") + @ApiResponses(value = { + @ApiResponse(code = 200, message = "Success"), + @ApiResponse(code = 401, message = "Unauthorized"), + @ApiResponse(code = 500, message = "Failure")}) + @RequestMapping(path = "/upload", method = RequestMethod.POST, produces = APPLICATION_JSON_UTF8_VALUE) + public void upload(@RequestParam("file") MultipartFile file, + @RequestParam("modName") String modName, + @RequestParam("apiKey") String apiKey + ) throws Exception { + if (!apiKey.equals(apiProperties.getDeployment().getTestingExeUploadKey())) { + throw new ApiException(new Error(ErrorCode.API_KEY_INVALID)); + } + String extension = Files.getFileExtension(file.getOriginalFilename()); + if (!apiProperties.getDeployment().getAllowedExeExtension().equals(extension)) { + throw new ApiException( + new Error(ErrorCode.UPLOAD_INVALID_FILE_EXTENSIONS, apiProperties.getDeployment().getAllowedExeExtension()) + ); + } + + log.info("Uploading exe file '{}' to '{}' directory", file.getOriginalFilename(), modName); + + exeUploaderService.processUpload(file.getBytes(), modName); + } +} diff --git a/src/main/java/com/faforever/api/deployment/ExeUploaderService.java b/src/main/java/com/faforever/api/deployment/ExeUploaderService.java new file mode 100644 index 000000000..33c4d7480 --- /dev/null +++ b/src/main/java/com/faforever/api/deployment/ExeUploaderService.java @@ -0,0 +1,95 @@ +package com.faforever.api.deployment; + +import com.faforever.api.config.FafApiProperties; +import com.faforever.api.content.ContentService; +import com.faforever.api.error.ApiException; +import com.faforever.api.error.Error; +import com.faforever.api.error.ErrorCode; +import com.faforever.api.featuredmods.FeaturedModFile; +import com.faforever.api.featuredmods.FeaturedModService; +import com.faforever.api.utils.FilePermissionUtil; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.Assert; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.Collections; +import java.util.List; + +import static com.google.common.hash.Hashing.md5; +import static com.google.common.io.Files.hash; +import static java.nio.file.Files.createDirectories; + +/** + * remove this shit + */ +@Service +@Slf4j +public class ExeUploaderService { + private final ContentService contentService; + private final FafApiProperties apiProperties; + private final FeaturedModService featuredModService; + + public ExeUploaderService( + ContentService contentService, + FafApiProperties apiProperties, + FeaturedModService featuredModService + ) { + this.contentService = contentService; + this.apiProperties = apiProperties; + this.featuredModService = featuredModService; + } + + @Transactional + @SneakyThrows + public void processUpload(byte[] bytes, String modName) { + checkAllowedBranchName(modName); + FeaturedModFile featuredModFile = featuredModService.getFile(modName, null, "ForgedAlliance.exe"); + featuredModFile.setName(String.format("ForgedAlliance.%d.exe", featuredModFile.getVersion())); + Path uploadedFile = this.upload( + bytes, + featuredModFile.getName(), + modName + ); + featuredModFile.setMd5(hash(uploadedFile.toFile(), md5()).toString()); + + List featuredModFiles = Collections.singletonList(featuredModFile); + featuredModService.save(modName, (short) featuredModFile.getVersion(), featuredModFiles); + } + + @SneakyThrows + public Path upload(byte[] exeData, String fileName, String modName) { + Assert.isTrue(exeData.length > 0, "data of 'ForgedAlliance.exe' must not be empty"); + + Path tempDir = contentService.createTempDir(); + Path temporaryFile = tempDir.resolve(fileName); + Files.write(temporaryFile, exeData); + + Path copyTo = Paths.get(getCopyToPath(modName, fileName)); + createDirectories(copyTo.getParent(), FilePermissionUtil.directoryPermissionFileAttributes()); + return Files.copy( + temporaryFile, + copyTo, + StandardCopyOption.REPLACE_EXISTING + ); + } + + private void checkAllowedBranchName(String modName) throws ApiException { + if (!"fafbeta".equals(modName) && !"fafdevelop".equals(modName)) { + throw new ApiException(new Error(ErrorCode.INVALID_FEATURED_MOD, modName)); + } + } + + private String getCopyToPath(String modName, String fileName) { + String copyTo = apiProperties.getDeployment().getForgedAllianceBetaExePath(); + if ("fafdevelop".equals(modName)) { + copyTo = apiProperties.getDeployment().getForgedAllianceDevelopExePath(); + } + return copyTo + "/" + fileName; + } +} diff --git a/src/main/java/com/faforever/api/deployment/LegacyFeaturedModDeploymentTask.java b/src/main/java/com/faforever/api/deployment/LegacyFeaturedModDeploymentTask.java index 145793151..18e10c5d0 100644 --- a/src/main/java/com/faforever/api/deployment/LegacyFeaturedModDeploymentTask.java +++ b/src/main/java/com/faforever/api/deployment/LegacyFeaturedModDeploymentTask.java @@ -29,18 +29,9 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.nio.file.FileVisitResult; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.StandardCopyOption; +import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.OptionalInt; +import java.util.*; import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -108,7 +99,10 @@ public void run() { Deployment deployment = apiProperties.getDeployment(); Path targetFolder = Paths.get(deployment.getFeaturedModsTargetDirectory(), String.format(deployment.getFilesDirectoryFormat(), modName)); List files = packageDirectories(repositoryDirectory, version, fileIds, targetFolder); - createPatchedExe(version, fileIds, targetFolder).ifPresent(files::add); + + if ("faf".equals(modName)) { + createPatchedExe(version, fileIds, targetFolder).ifPresent(files::add); + } if (files.isEmpty()) { log.warn("Could not find any files to deploy. Is the configuration correct?"); @@ -161,7 +155,7 @@ private Optional createPatchedExe(short version, Map } private short readModVersion(Path modPath) { - return (short) Integer.parseInt(new ModReader().readDirectory(modPath).getVersion().toString()); + return Short.parseShort(new ModReader().readDirectory(modPath).getVersion().toString()); } private void verifyVersion(int version, boolean allowOverride, String modName) { diff --git a/src/main/java/com/faforever/api/error/ErrorCode.java b/src/main/java/com/faforever/api/error/ErrorCode.java index 37a96e627..9cfe5c131 100644 --- a/src/main/java/com/faforever/api/error/ErrorCode.java +++ b/src/main/java/com/faforever/api/error/ErrorCode.java @@ -97,7 +97,9 @@ public enum ErrorCode { MAP_NAME_TOO_SHORT(187, "Map name invalid", "The map name must have at least {0, number} characters, was: {1, number}"), MAP_NAME_DOES_NOT_START_WITH_LETTER(188, "Map name invalid", "The map name has to begin with a letter"), PARSING_LUA_FILE_FAILED(189, "Parsing lua files failed", "During the parsing of the lua file an error occured: {0}"), - NO_RUSH_RADIUS_MISSING(190, "No rush radius missing", "The scenario file must specify a no rush radius"); + NO_RUSH_RADIUS_MISSING(190, "No rush radius missing", "The scenario file must specify a no rush radius"), + INVALID_FEATURED_MOD(191, "Invalid featured mod name", "The featured mod name ''{0}'' is not allowed in this context."), + API_KEY_INVALID(192, "Api key is invalid", "The api key is invalid."); private final int code; private final String title; diff --git a/src/main/java/com/faforever/api/featuredmods/FeaturedModService.java b/src/main/java/com/faforever/api/featuredmods/FeaturedModService.java index 0ef43283e..e39188b4e 100644 --- a/src/main/java/com/faforever/api/featuredmods/FeaturedModService.java +++ b/src/main/java/com/faforever/api/featuredmods/FeaturedModService.java @@ -22,6 +22,10 @@ public FeaturedModService(FeaturedModRepository featuredModRepository, LegacyFea this.legacyFeaturedModFileRepository = legacyFeaturedModFileRepository; } + public FeaturedModFile getFile(String modName, Integer version, String fileName) { + return legacyFeaturedModFileRepository.getFile(modName, version, fileName); + } + @Cacheable(FEATURED_MOD_FILES_CACHE_NAME) public List getFiles(String modName, @Nullable Integer version) { return legacyFeaturedModFileRepository.getFiles(modName, version); diff --git a/src/main/java/com/faforever/api/featuredmods/LegacyFeaturedModFileRepository.java b/src/main/java/com/faforever/api/featuredmods/LegacyFeaturedModFileRepository.java index b168fd40d..c938380c1 100644 --- a/src/main/java/com/faforever/api/featuredmods/LegacyFeaturedModFileRepository.java +++ b/src/main/java/com/faforever/api/featuredmods/LegacyFeaturedModFileRepository.java @@ -19,9 +19,18 @@ public LegacyFeaturedModFileRepository(EntityManager entityManager) { this.entityManager = entityManager; } - @SuppressWarnings("unchecked") + public FeaturedModFile getFile(String modName, Integer version, String fileName) { + List files = getFiles(modName, version, fileName); + return files.get(0); + } + public List getFiles(String modName, Integer version) { - // Please shoot me. + return getFiles(modName, version, null); + } + + @SuppressWarnings("unchecked") + public List getFiles(String modName, Integer version, String fileName) { + // Please shoot me. +1 String innerModName = "ladder1v1".equals(modName) ? "faf" : modName; verifyModName(innerModName); @@ -35,7 +44,7 @@ public List getFiles(String modName, Integer version) { " file.fileId AS `fileId`," + " file.id AS `id`," + " file.name AS `fileName`," + - " 'updates_%1$s_files' AS `folderName`" + + " 'updates_%1$s_files' AS `folderName` " + "FROM updates_%1$s_files file" + " INNER JOIN" + " (" + @@ -49,11 +58,18 @@ public List getFiles(String modName, Integer version) { " ON file.fileId = latest.fileId" + " AND file.version = latest.version" + " INNER JOIN updates_%1$s b" + - " ON b.id = file.fileId;", innerModName, (version == null ? "" : "WHERE version <= :version")), FeaturedModFile.class); + " ON b.id = file.fileId %3$s ", + innerModName, + (version == null ? "" : "WHERE version <= :version"), + (fileName == null ? "" : " AND filename = :filename") + ), FeaturedModFile.class); if (version != null) { query.setParameter("version", version); } + if (fileName != null) { + query.setParameter("filename", fileName); + } return (List) query.getResultList(); } diff --git a/src/main/resources/config/application-dev.yml b/src/main/resources/config/application-dev.yml index c6ccaba5e..9d3dd6fa8 100644 --- a/src/main/resources/config/application-dev.yml +++ b/src/main/resources/config/application-dev.yml @@ -29,6 +29,9 @@ faf-api: forged-alliance-exe-path: ${FORGED_ALLIANCE_EXE_PATH} repositories-directory: ${REPOSITORIES_DIRECTORY:build/cache/repos} featured-mods-target-directory: ${FEATURED_MODS_TARGET_DIRECTORY:build/cache/deployment} + testing-exe-upload-key: ${TESTING_EXE_UPLOAD_KEY:"banana"} + forged-alliance-beta-exe-path: ${EXE_UPLOAD_BETA_PATH:"build/exe/beta"} + forged-alliance-develop-exe-path: ${EXE_UPLOAD_DEVELOP_PATH:"build/exe/develop"} clan: website-url-format: ${CLAN_WEBSITE_URL_FORMAT:http://clans.test.faforever.com/clan/%s} mail: diff --git a/src/main/resources/config/application-prod.yml b/src/main/resources/config/application-prod.yml index dabd06f4e..3021be575 100644 --- a/src/main/resources/config/application-prod.yml +++ b/src/main/resources/config/application-prod.yml @@ -30,6 +30,9 @@ faf-api: forged-alliance-exe-path: ${FORGED_ALLIANCE_EXE_PATH} repositories-directory: ${REPOSITORIES_DIRECTORY} featured-mods-target-directory: ${FEATURED_MODS_TARGET_DIRECTORY} + testing-exe-upload-key: ${TESTING_EXE_UPLOAD_KEY} + forged-alliance-beta-exe-path: ${EXE_UPLOAD_BETA_PATH} + forged-alliance-develop-exe-path: ${EXE_UPLOAD_DEVELOP_PATH} clan: website-url-format: ${CLAN_WEBSITE_URL_FORMAT} mail: diff --git a/src/test/java/com/faforever/api/deployment/ExeUploaderControllerTest.java b/src/test/java/com/faforever/api/deployment/ExeUploaderControllerTest.java new file mode 100644 index 000000000..f6857d494 --- /dev/null +++ b/src/test/java/com/faforever/api/deployment/ExeUploaderControllerTest.java @@ -0,0 +1,100 @@ +package com.faforever.api.deployment; + +import com.faforever.api.config.FafApiProperties; +import com.faforever.api.config.TestWebSecurityConfig; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; + +import javax.inject.Inject; + +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.fileUpload; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@RunWith(SpringRunner.class) +@WebMvcTest(ExeUploaderController.class) +@Import(TestWebSecurityConfig.class) +public class ExeUploaderControllerTest { + + public static final String API_KEY = "banana"; + private MockMvc mvc; + @MockBean + private ExeUploaderService exeUploaderService; + @MockBean + private FafApiProperties fafApiProperties; + @MockBean + private FafApiProperties.Deployment deployment; + + @Mock + private MockMultipartFile file; + + @Inject + public void init(MockMvc mvc) { + this.mvc = mvc; + } + + @Before + public void setUp() { + when(fafApiProperties.getDeployment()).thenReturn(deployment); + when(deployment.getAllowedExeExtension()).thenReturn("exe"); + when(deployment.getTestingExeUploadKey()).thenReturn(API_KEY); + file = new MockMultipartFile("file", + "ForgedAlliance.exe", + "application/octet-stream", + new byte[]{1, 2, 3, 4}); + } + + @Test + public void testSuccessUpload() throws Exception { + this.mvc.perform(fileUpload("/exe/upload") + .file(file) + .param("modName", "fafbeta") + .param("apiKey", API_KEY) + ).andExpect(status().isOk()); + } + + @Test + public void testBadRequestUploadNoModName() throws Exception { + this.mvc.perform(fileUpload("/exe/upload") + .file(file) + .param("apiKey", API_KEY) + ).andExpect(status().isBadRequest()); + } + + @Test + public void testBadRequestUploadNoFile() throws Exception { + this.mvc.perform(fileUpload("/exe/upload") + .param("modName", "fafbeta2222") + .param("apiKey", API_KEY) + ).andExpect(status().isBadRequest()); + } + + @Test + public void testBadRequestUploadFileWithWrongExeExtension() throws Exception { + MockMultipartFile file = new MockMultipartFile("file", + "ForgedAlliance.zip", + "application/octet-stream", + new byte[]{1, 2, 3, 4}); + this.mvc.perform(fileUpload("/exe/upload") + .file(file) + .param("modName", "fafbeta") + .param("apiKey", API_KEY) + ).andExpect(status().is4xxClientError()); + } + + @Test + public void testBadRequestUploadWithoutApiKey() throws Exception { + this.mvc.perform(fileUpload("/exe/upload") + .file(file) + .param("modName", "fafbeta") + ).andExpect(status().isBadRequest()); + } +} diff --git a/src/test/java/com/faforever/api/deployment/ExeUploaderServiceTest.java b/src/test/java/com/faforever/api/deployment/ExeUploaderServiceTest.java new file mode 100644 index 000000000..4d23c1701 --- /dev/null +++ b/src/test/java/com/faforever/api/deployment/ExeUploaderServiceTest.java @@ -0,0 +1,125 @@ +package com.faforever.api.deployment; + +import com.faforever.api.config.FafApiProperties; +import com.faforever.api.content.ContentService; +import com.faforever.api.error.ApiException; +import com.faforever.api.featuredmods.FeaturedModFile; +import com.faforever.api.featuredmods.FeaturedModService; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; + +import static junit.framework.TestCase.assertTrue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.*; + +@RunWith(MockitoJUnitRunner.class) // Is this right? +public class ExeUploaderServiceTest { + private ExeUploaderService instance; + + @Rule + public final TemporaryFolder temporaryDirectory = new TemporaryFolder(); + @Rule + public final TemporaryFolder finalDirectory = new TemporaryFolder(); + + @Mock + private ContentService contentService; + @Mock + private FafApiProperties apiProperties; + @Mock + private FafApiProperties.Deployment deployment; + @Mock + private FeaturedModService featuredModService; + private byte[] bytes; + + + private FeaturedModFile featuredModFile; + + @Before + public void setUp() { + instance = new ExeUploaderService(contentService, apiProperties, featuredModService); + + when(apiProperties.getDeployment()).thenReturn(deployment); + bytes = new byte[]{1, 2, 3, 4}; + when(contentService.createTempDir()).thenReturn(temporaryDirectory.getRoot().toPath()); + + featuredModFile = new FeaturedModFile(); + featuredModFile.setName("ForgedAlliance.exe"); + featuredModFile.setVersion(1); + featuredModFile.setFileId((short) 1); + } + + @After + public void after() { + verifyNoMoreInteractions(contentService, apiProperties, deployment, featuredModService); + } + + @Test + public void testProcessUploadBeta() { + String modName = "fafbeta"; + + String finalExeDestination = finalDirectory.getRoot().getAbsolutePath() + "/ForgedAlliance.1.exe"; + when(deployment.getForgedAllianceBetaExePath()).thenReturn(finalExeDestination); + + when(featuredModService.getFile(modName, null, "ForgedAlliance.exe")).thenReturn( + featuredModFile); + instance.processUpload(bytes, modName); + + // File was created + assertTrue(Files.exists(Paths.get(finalExeDestination))); + // File was updated in database + ArgumentCaptor> modFilesCaptor = ArgumentCaptor.forClass(List.class); + verify(featuredModService).save(eq(modName), eq((short) featuredModFile.getVersion()), modFilesCaptor.capture()); + assertThat(modFilesCaptor.getValue().size(), is(1)); + assertThat(modFilesCaptor.getValue(), hasItem(featuredModFile)); + + verify(contentService).createTempDir(); + verify(apiProperties, atLeastOnce()).getDeployment(); + verify(deployment).getForgedAllianceBetaExePath(); + verify(featuredModService).getFile(modName, null, "ForgedAlliance.exe"); + } + + @Test + public void testProcessUploadDevelop() { + String modName = "fafdevelop"; + + String finalExeDestination = finalDirectory.getRoot().getAbsolutePath() + "/ForgedAlliance.1.exe"; + when(deployment.getForgedAllianceDevelopExePath()).thenReturn(finalExeDestination); + + when(featuredModService.getFile(modName, null, "ForgedAlliance.exe")).thenReturn( + featuredModFile); + instance.processUpload(bytes, modName); + + // File was created + assertTrue(Files.exists(Paths.get(finalExeDestination))); + // File was updated in database + ArgumentCaptor> modFilesCaptor = ArgumentCaptor.forClass(List.class); + verify(featuredModService).save(eq(modName), eq((short) featuredModFile.getVersion()), modFilesCaptor.capture()); + assertThat(modFilesCaptor.getValue().size(), is(1)); + assertThat(modFilesCaptor.getValue(), hasItem(featuredModFile)); + + verify(contentService).createTempDir(); + verify(apiProperties, atLeastOnce()).getDeployment(); + verify(deployment).getForgedAllianceBetaExePath(); + verify(deployment).getForgedAllianceDevelopExePath(); + verify(featuredModService).getFile(modName, null, "ForgedAlliance.exe"); + } + + @Test(expected = ApiException.class) + public void testProcessUploadIsForbidden() { + String modName = "faf"; + instance.processUpload(bytes, modName); + } +} From 31b7b114ac54fd0357e621da6d09800e82f922c1 Mon Sep 17 00:00:00 2001 From: Konstantin Kalinovsky Date: Sat, 31 Aug 2019 16:44:07 +0200 Subject: [PATCH 02/10] Fixing linux wrong directory names --- src/inttest/resources/config/application.yml | 6 +++--- src/main/resources/config/application-dev.yml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/inttest/resources/config/application.yml b/src/inttest/resources/config/application.yml index 351409c24..923d677cf 100644 --- a/src/inttest/resources/config/application.yml +++ b/src/inttest/resources/config/application.yml @@ -70,9 +70,9 @@ faf-api: featured-mods-target-directory: "build/cache/mods" repositories-directory: "" forged-alliance-exe-path: "build/exe/faf" - testing-exe-upload-key: "banana" - forged-alliance-beta-exe-path: "build/exe/beta" - forged-alliance-develop-exe-path: "build/exe/develop" + testing-exe-upload-key: banana + forged-alliance-beta-exe-path: build/exe/beta + forged-alliance-develop-exe-path: build/exe/develop featured-mod: file-url-format: "file://%s/%s" diff --git a/src/main/resources/config/application-dev.yml b/src/main/resources/config/application-dev.yml index 9d3dd6fa8..dda210e3c 100644 --- a/src/main/resources/config/application-dev.yml +++ b/src/main/resources/config/application-dev.yml @@ -30,8 +30,8 @@ faf-api: repositories-directory: ${REPOSITORIES_DIRECTORY:build/cache/repos} featured-mods-target-directory: ${FEATURED_MODS_TARGET_DIRECTORY:build/cache/deployment} testing-exe-upload-key: ${TESTING_EXE_UPLOAD_KEY:"banana"} - forged-alliance-beta-exe-path: ${EXE_UPLOAD_BETA_PATH:"build/exe/beta"} - forged-alliance-develop-exe-path: ${EXE_UPLOAD_DEVELOP_PATH:"build/exe/develop"} + forged-alliance-beta-exe-path: ${EXE_UPLOAD_BETA_PATH:build/exe/beta} + forged-alliance-develop-exe-path: ${EXE_UPLOAD_DEVELOP_PATH:build/exe/develop} clan: website-url-format: ${CLAN_WEBSITE_URL_FORMAT:http://clans.test.faforever.com/clan/%s} mail: From d95aff77a5f42a8f4014de7454a5a645f50119db Mon Sep 17 00:00:00 2001 From: Konstantin Kalinovsky Date: Sat, 31 Aug 2019 16:47:00 +0200 Subject: [PATCH 03/10] Fixing stuff before review --- .travis.yml | 2 +- src/main/resources/config/application-dev.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6aa37d22d..f1dd9e045 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ git: env: global: - - GRADLE_CLI_OPTS="--console=plain --stacktrace --full-stacktrace" + - GRADLE_CLI_OPTS="--console=plain --stacktrace --quiet" before_install: - sudo /etc/init.d/mysql stop diff --git a/src/main/resources/config/application-dev.yml b/src/main/resources/config/application-dev.yml index dda210e3c..69ee5847c 100644 --- a/src/main/resources/config/application-dev.yml +++ b/src/main/resources/config/application-dev.yml @@ -29,7 +29,7 @@ faf-api: forged-alliance-exe-path: ${FORGED_ALLIANCE_EXE_PATH} repositories-directory: ${REPOSITORIES_DIRECTORY:build/cache/repos} featured-mods-target-directory: ${FEATURED_MODS_TARGET_DIRECTORY:build/cache/deployment} - testing-exe-upload-key: ${TESTING_EXE_UPLOAD_KEY:"banana"} + testing-exe-upload-key: ${TESTING_EXE_UPLOAD_KEY:banana} forged-alliance-beta-exe-path: ${EXE_UPLOAD_BETA_PATH:build/exe/beta} forged-alliance-develop-exe-path: ${EXE_UPLOAD_DEVELOP_PATH:build/exe/develop} clan: From 36be955b9a48b7632db868dd6bfd6e06f6ff64e8 Mon Sep 17 00:00:00 2001 From: Konstantin Kalinovsky Date: Sun, 1 Sep 2019 17:08:06 +0200 Subject: [PATCH 04/10] Fixes after review --- src/inttest/resources/sql/prepGameData.sql | 2 -- .../com/faforever/api/deployment/ExeUploaderService.java | 7 ++----- .../api/featuredmods/LegacyFeaturedModFileRepository.java | 1 - .../faforever/api/deployment/ExeUploaderServiceTest.java | 6 +----- 4 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/inttest/resources/sql/prepGameData.sql b/src/inttest/resources/sql/prepGameData.sql index ea644532d..3d6dbee40 100644 --- a/src/inttest/resources/sql/prepGameData.sql +++ b/src/inttest/resources/sql/prepGameData.sql @@ -8,7 +8,5 @@ VALUES (25, 'coop', 'Coop', 'Multiplayer campaign games', 1, 'https://github.com/FAForever/fa-coop.git', 'master', 'cop', TRUE); --- INSERT INTO `game_validity` (`id`, `message`) VALUES (0, 'Valid') ON DUPLICATE KEY UPDATE `id` = VALUES(id); - INSERT INTO game_stats (id, startTime, gameName, gameType, gameMod, `host`, mapId, validity) VALUES (1, NOW(), 'Test game', '0', 6, 1, 1, 0); diff --git a/src/main/java/com/faforever/api/deployment/ExeUploaderService.java b/src/main/java/com/faforever/api/deployment/ExeUploaderService.java index 33c4d7480..f42d81a5e 100644 --- a/src/main/java/com/faforever/api/deployment/ExeUploaderService.java +++ b/src/main/java/com/faforever/api/deployment/ExeUploaderService.java @@ -25,9 +25,6 @@ import static com.google.common.io.Files.hash; import static java.nio.file.Files.createDirectories; -/** - * remove this shit - */ @Service @Slf4j public class ExeUploaderService { @@ -85,11 +82,11 @@ private void checkAllowedBranchName(String modName) throws ApiException { } } - private String getCopyToPath(String modName, String fileName) { + private Path getCopyToPath(String modName, String fileName) { String copyTo = apiProperties.getDeployment().getForgedAllianceBetaExePath(); if ("fafdevelop".equals(modName)) { copyTo = apiProperties.getDeployment().getForgedAllianceDevelopExePath(); } - return copyTo + "/" + fileName; + return Paths.get(copyTo, fileName); } } diff --git a/src/main/java/com/faforever/api/featuredmods/LegacyFeaturedModFileRepository.java b/src/main/java/com/faforever/api/featuredmods/LegacyFeaturedModFileRepository.java index c938380c1..c5d2d77dd 100644 --- a/src/main/java/com/faforever/api/featuredmods/LegacyFeaturedModFileRepository.java +++ b/src/main/java/com/faforever/api/featuredmods/LegacyFeaturedModFileRepository.java @@ -30,7 +30,6 @@ public List getFiles(String modName, Integer version) { @SuppressWarnings("unchecked") public List getFiles(String modName, Integer version, String fileName) { - // Please shoot me. +1 String innerModName = "ladder1v1".equals(modName) ? "faf" : modName; verifyModName(innerModName); diff --git a/src/test/java/com/faforever/api/deployment/ExeUploaderServiceTest.java b/src/test/java/com/faforever/api/deployment/ExeUploaderServiceTest.java index 4d23c1701..1c5a14709 100644 --- a/src/test/java/com/faforever/api/deployment/ExeUploaderServiceTest.java +++ b/src/test/java/com/faforever/api/deployment/ExeUploaderServiceTest.java @@ -25,7 +25,7 @@ import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.*; -@RunWith(MockitoJUnitRunner.class) // Is this right? +@RunWith(MockitoJUnitRunner.class) public class ExeUploaderServiceTest { private ExeUploaderService instance; @@ -77,9 +77,7 @@ public void testProcessUploadBeta() { featuredModFile); instance.processUpload(bytes, modName); - // File was created assertTrue(Files.exists(Paths.get(finalExeDestination))); - // File was updated in database ArgumentCaptor> modFilesCaptor = ArgumentCaptor.forClass(List.class); verify(featuredModService).save(eq(modName), eq((short) featuredModFile.getVersion()), modFilesCaptor.capture()); assertThat(modFilesCaptor.getValue().size(), is(1)); @@ -102,9 +100,7 @@ public void testProcessUploadDevelop() { featuredModFile); instance.processUpload(bytes, modName); - // File was created assertTrue(Files.exists(Paths.get(finalExeDestination))); - // File was updated in database ArgumentCaptor> modFilesCaptor = ArgumentCaptor.forClass(List.class); verify(featuredModService).save(eq(modName), eq((short) featuredModFile.getVersion()), modFilesCaptor.capture()); assertThat(modFilesCaptor.getValue().size(), is(1)); From 0a2d0239c1ba827f44d3e5e770a93a2f748b7de4 Mon Sep 17 00:00:00 2001 From: Konstantin Kalinovsky Date: Sun, 1 Sep 2019 17:16:53 +0200 Subject: [PATCH 05/10] Path fixed --- .../java/com/faforever/api/deployment/ExeUploaderService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/faforever/api/deployment/ExeUploaderService.java b/src/main/java/com/faforever/api/deployment/ExeUploaderService.java index f42d81a5e..f15170598 100644 --- a/src/main/java/com/faforever/api/deployment/ExeUploaderService.java +++ b/src/main/java/com/faforever/api/deployment/ExeUploaderService.java @@ -67,7 +67,7 @@ public Path upload(byte[] exeData, String fileName, String modName) { Path temporaryFile = tempDir.resolve(fileName); Files.write(temporaryFile, exeData); - Path copyTo = Paths.get(getCopyToPath(modName, fileName)); + Path copyTo = getCopyToPath(modName, fileName); createDirectories(copyTo.getParent(), FilePermissionUtil.directoryPermissionFileAttributes()); return Files.copy( temporaryFile, From 11717abdf95c9f6587e2f5fb8d275884f040249c Mon Sep 17 00:00:00 2001 From: Konstantin Kalinovsky Date: Sun, 8 Sep 2019 21:34:06 +0200 Subject: [PATCH 06/10] Fixes after review, changed bytes read to stream, updated fixtures for test, now they're not dropping tables, added some tests --- .../deployment/ExeUploadControllerTest.java | 9 + .../resources/sql/cleanFeaturedMods.sql | 4 +- .../resources/sql/prepFeaturedMods.sql | 869 +----------------- .../api/deployment/ExeUploaderController.java | 2 +- .../api/deployment/ExeUploaderService.java | 29 +- .../LegacyFeaturedModFileRepository.java | 1 + .../deployment/ExeUploaderServiceTest.java | 13 +- 7 files changed, 44 insertions(+), 883 deletions(-) diff --git a/src/inttest/java/com/faforever/api/deployment/ExeUploadControllerTest.java b/src/inttest/java/com/faforever/api/deployment/ExeUploadControllerTest.java index af4035a70..1424a12b7 100644 --- a/src/inttest/java/com/faforever/api/deployment/ExeUploadControllerTest.java +++ b/src/inttest/java/com/faforever/api/deployment/ExeUploadControllerTest.java @@ -78,4 +78,13 @@ public void testBadRequestUploadWithoutApiKey() throws Exception { .param("modName", "fafbeta") ).andExpect(status().is4xxClientError()); } + + @Test + public void testBadRequestUploadWithWrongApiKey() throws Exception { + this.mockMvc.perform(fileUpload("/exe/upload") + .file(file) + .param("modName", "fafbeta") + .param("apiKey", "not a banana") + ).andExpect(status().is4xxClientError()); + } } diff --git a/src/inttest/resources/sql/cleanFeaturedMods.sql b/src/inttest/resources/sql/cleanFeaturedMods.sql index c2280ad33..97f1dbea2 100644 --- a/src/inttest/resources/sql/cleanFeaturedMods.sql +++ b/src/inttest/resources/sql/cleanFeaturedMods.sql @@ -1,5 +1,5 @@ -DROP TABLE updates_fafdevelop_files; +DELETE FROM updates_fafdevelop_files; DELETE FROM updates_fafbeta_files; -DROP TABLE updates_fafdevelop; +DELETE FROM updates_fafdevelop; DELETE FROM updates_fafbeta; DELETE FROM game_featuredMods; diff --git a/src/inttest/resources/sql/prepFeaturedMods.sql b/src/inttest/resources/sql/prepFeaturedMods.sql index d028ae004..8bd0e368f 100644 --- a/src/inttest/resources/sql/prepFeaturedMods.sql +++ b/src/inttest/resources/sql/prepFeaturedMods.sql @@ -1,3 +1,5 @@ +DELETE FROM updates_fafdevelop_files; +DELETE FROM updates_fafdevelop; DELETE FROM updates_fafbeta_files; DELETE FROM updates_fafbeta; DELETE FROM reported_user; @@ -5,882 +7,19 @@ DELETE FROM moderation_report; DELETE FROM game_stats; DELETE FROM game_featuredMods; -DROP TABLE IF EXISTS `updates_fafdevelop_files`; -DROP TABLE IF EXISTS `updates_fafdevelop`; - -CREATE TABLE IF NOT EXISTS `updates_fafdevelop_files` ( - `id` int(10) unsigned NOT NULL AUTO_INCREMENT, - `fileId` smallint(5) unsigned NOT NULL, - `version` int(11) NOT NULL, - `name` varchar(45) NOT NULL, - `md5` varchar(45) NOT NULL, - `obselete` tinyint(1) NOT NULL DEFAULT '0', - PRIMARY KEY (`id`), - KEY `fileId` (`fileId`) -) ENGINE=InnoDB; - -CREATE TABLE IF NOT EXISTS `updates_fafdevelop` ( - `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT, - `filename` varchar(45) NOT NULL, - `path` varchar(45) NOT NULL, - UNIQUE KEY `id` (`id`) -) ENGINE=InnoDB; - - - INSERT INTO `game_featuredMods` (`id`, `gamemod`, `description`, `name`, `publish`, `order`, `git_url`, `git_branch`, `file_extension`, `allow_override`, `deployment_webhook`) VALUES (1, 'faf', 'Forged Alliance Forever', 'FAF', 1, 0, 'https://github.com/FAForever/fa.git', 'deploy/faf', 'nx2', 0, NULL), (27, 'fafbeta', 'Beta version of the next FAF patch', 'FAF Beta', 1, 2, 'https://github.com/FAForever/fa.git', 'deploy/fafbeta', 'nx4', 1, NULL), (28, 'fafdevelop', 'Updated frequently for testing the upcoming game Patch', 'FAF Develop', 1, 11, 'https://github.com/FAForever/fa.git', 'deploy/fafdevelop', 'nx5', 1, NULL); INSERT INTO `updates_fafbeta` (`id`, `filename`, `path`) VALUES -(1, 'ForgedAlliance.exe', 'bin'), -(2, 'init_fafbeta.lua', 'bin'), -(11, 'effects.nx4', 'gamedata'), -(12, 'env.nx4', 'gamedata'), -(13, 'loc.nx4', 'gamedata'), -(14, 'lua.nx4', 'gamedata'), -(15, 'meshes.nx4', 'gamedata'), -(17, 'modules.nx4', 'gamedata'), -(18, 'projectiles.nx4', 'gamedata'), -(19, 'schook.nx4', 'gamedata'), -(20, 'textures.nx4', 'gamedata'), -(21, 'units.nx4', 'gamedata'), -(22, 'etc.nx4', 'gamedata'); +(1, 'ForgedAlliance.exe', 'bin'); INSERT INTO `updates_fafbeta_files` (`id`, `fileId`, `version`, `name`, `md5`, `obselete`) VALUES -(1, 2, 3652, 'init_fafbeta.lua', '0e58a7ecf1609b6453d62ceeb44f89c9', 0), -(2, 11, 3652, 'effects_0.3652.nxt', '31d289288784211fbf57fb639ce88a64', 0), -(3, 12, 3652, 'env_0.3652.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(4, 13, 3652, 'loc_0.3652.nxt', '97464f661ea84600543bbde05d12dd73', 0), -(5, 14, 3652, 'lua_0.3652.nxt', '7642fea8dfc908b490ec60db67ece06c', 0), -(6, 15, 3652, 'meshes_0.3652.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(7, 17, 3652, 'modules_0.3652.nxt', '6a81c76137fe7fe6579c55aabf03dbb7', 0), -(8, 18, 3652, 'projectiles_0.3652.nxt', 'ecbff75d5ee54dbea7baffec71a214b4', 0), -(9, 19, 3652, 'schook_0.3652.nxt', 'd70eb26a7f9eeddc28df711b839156b0', 0), -(10, 20, 3652, 'textures_0.3652.nxt', '1e0dd68c463f300234423a2346e5a979', 0), -(11, 21, 3652, 'units_0.3652.nxt', '0e91d45f764d27c7924a09384a47e60a', 0), -(12, 22, 3652, 'etc_0.3652.nxt', 'cb38d0bc92e4d01b8afce879bd3ab669', 0), -(24, 11, 3653, 'effects_0.3653.nxt', '7823e173c153ce3fc2977c68c54f1a3c', 0), -(25, 12, 3653, 'env_0.3653.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(26, 13, 3653, 'loc_0.3653.nxt', '85b23808ab6478b2fd303da8248bf738', 0), -(27, 14, 3653, 'lua_0.3653.nxt', 'f72663f46e1b15ec45314221a82b4d77', 0), -(28, 15, 3653, 'meshes_0.3653.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(29, 17, 3653, 'modules_0.3653.nxt', 'e20fec02e2251b5085cbe4096b7769a9', 0), -(30, 18, 3653, 'projectiles_0.3653.nxt', 'c1a7b415a3f5477470150e91580ccf19', 0), -(31, 19, 3653, 'schook_0.3653.nxt', 'fc661665d45af0976f5a70de863046a2', 0), -(32, 20, 3653, 'textures_0.3653.nxt', 'b72caa7d4e76ff71471badf55b8ee800', 0), -(33, 21, 3653, 'units_0.3653.nxt', '1255955a81ee7d73862ed6a54fb0d42f', 0), -(34, 22, 3653, 'etc_0.3653.nxt', '3277eb482e12fe0231fe692f356d935b', 0), -(35, 11, 3654, 'effects_0.3654.nxt', '7823e173c153ce3fc2977c68c54f1a3c', 0), -(36, 12, 3654, 'env_0.3654.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(37, 13, 3654, 'loc_0.3654.nxt', '6e9f6a5e148e94a8a292ed58d13f90d4', 0), -(38, 14, 3654, 'lua_0.3654.nxt', 'aaae2e5185b8ccb555296cb36f5af460', 0), -(39, 15, 3654, 'meshes_0.3654.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(40, 17, 3654, 'modules_0.3654.nxt', 'e20fec02e2251b5085cbe4096b7769a9', 0), -(41, 18, 3654, 'projectiles_0.3654.nxt', 'c1a7b415a3f5477470150e91580ccf19', 0), -(42, 19, 3654, 'schook_0.3654.nxt', 'fc661665d45af0976f5a70de863046a2', 0), -(43, 20, 3654, 'textures_0.3654.nxt', 'b72caa7d4e76ff71471badf55b8ee800', 0), -(44, 21, 3654, 'units_0.3654.nxt', '45e8a56d3d8f5750611961459f191c7a', 0), -(45, 22, 3654, 'etc_0.3654.nxt', '3277eb482e12fe0231fe692f356d935b', 0), -(46, 11, 3655, 'effects_0.3655.nxt', '7823e173c153ce3fc2977c68c54f1a3c', 0), -(47, 12, 3655, 'env_0.3655.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(48, 13, 3655, 'loc_0.3655.nxt', '6e9f6a5e148e94a8a292ed58d13f90d4', 0), -(49, 14, 3655, 'lua_0.3655.nxt', '5b34a8b361448cd3f515c96c4faca15b', 0), -(50, 15, 3655, 'meshes_0.3655.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(51, 17, 3655, 'modules_0.3655.nxt', 'e20fec02e2251b5085cbe4096b7769a9', 0), -(52, 18, 3655, 'projectiles_0.3655.nxt', 'c1a7b415a3f5477470150e91580ccf19', 0), -(53, 19, 3655, 'schook_0.3655.nxt', '122e008515f6a33e2d7936650373423e', 0), -(54, 20, 3655, 'textures_0.3655.nxt', 'ad4c595b8c0a0fb8b0e1e78ccf07c192', 0), -(55, 21, 3655, 'units_0.3655.nxt', '6fc20c5699bef0d18691fdf62cbb09d3', 0), -(56, 22, 3655, 'etc_0.3655.nxt', '3514201b87653551cf5db696556ec475', 0), -(57, 11, 3656, 'effects_0.3656.nxt', '7823e173c153ce3fc2977c68c54f1a3c', 0), -(58, 12, 3656, 'env_0.3656.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(59, 13, 3656, 'loc_0.3656.nxt', '670934cd424bc14e85c389c73e9f4175', 0), -(60, 14, 3656, 'lua_0.3656.nxt', '6dd341f362de25c336eaf963db1e2cad', 0), -(61, 15, 3656, 'meshes_0.3656.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(62, 17, 3656, 'modules_0.3656.nxt', 'e20fec02e2251b5085cbe4096b7769a9', 0), -(63, 18, 3656, 'projectiles_0.3656.nxt', 'c1a7b415a3f5477470150e91580ccf19', 0), -(64, 19, 3656, 'schook_0.3656.nxt', '122e008515f6a33e2d7936650373423e', 0), -(65, 20, 3656, 'textures_0.3656.nxt', 'ad4c595b8c0a0fb8b0e1e78ccf07c192', 0), -(66, 21, 3656, 'units_0.3656.nxt', 'a6413d206fc9db539bbe543adfc70b0f', 0), -(67, 22, 3656, 'etc_0.3656.nxt', '3514201b87653551cf5db696556ec475', 0), -(68, 11, 3657, 'effects_0.3657.nxt', '7823e173c153ce3fc2977c68c54f1a3c', 0), -(69, 12, 3657, 'env_0.3657.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(70, 13, 3657, 'loc_0.3657.nxt', '670934cd424bc14e85c389c73e9f4175', 0), -(71, 14, 3657, 'lua_0.3657.nxt', '225aefd5d4baa4e50c758cb06b880bb1', 0), -(72, 15, 3657, 'meshes_0.3657.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(73, 17, 3657, 'modules_0.3657.nxt', 'e20fec02e2251b5085cbe4096b7769a9', 0), -(74, 18, 3657, 'projectiles_0.3657.nxt', 'c1a7b415a3f5477470150e91580ccf19', 0), -(75, 19, 3657, 'schook_0.3657.nxt', '122e008515f6a33e2d7936650373423e', 0), -(76, 20, 3657, 'textures_0.3657.nxt', 'ad4c595b8c0a0fb8b0e1e78ccf07c192', 0), -(77, 21, 3657, 'units_0.3657.nxt', 'a6413d206fc9db539bbe543adfc70b0f', 0), -(78, 22, 3657, 'etc_0.3657.nxt', '3514201b87653551cf5db696556ec475', 0), -(79, 11, 3658, 'effects_0.3658.nxt', '7823e173c153ce3fc2977c68c54f1a3c', 0), -(80, 12, 3658, 'env_0.3658.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(81, 13, 3658, 'loc_0.3658.nxt', '670934cd424bc14e85c389c73e9f4175', 0), -(82, 14, 3658, 'lua_0.3658.nxt', 'b0f6c1a40bbc41e398ee9340ea14dd1f', 0), -(83, 15, 3658, 'meshes_0.3658.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(84, 17, 3658, 'modules_0.3658.nxt', 'e20fec02e2251b5085cbe4096b7769a9', 0), -(85, 18, 3658, 'projectiles_0.3658.nxt', 'c1a7b415a3f5477470150e91580ccf19', 0), -(86, 19, 3658, 'schook_0.3658.nxt', '122e008515f6a33e2d7936650373423e', 0), -(87, 20, 3658, 'textures_0.3658.nxt', 'ad4c595b8c0a0fb8b0e1e78ccf07c192', 0), -(88, 21, 3658, 'units_0.3658.nxt', 'a6413d206fc9db539bbe543adfc70b0f', 0), -(89, 22, 3658, 'etc_0.3658.nxt', '3514201b87653551cf5db696556ec475', 0), -(280, 11, 3659, 'effects_0.3659.nxt', '3758baad77531dd5323c766433412e91', 0), -(281, 12, 3659, 'env_0.3659.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(282, 13, 3659, 'loc_0.3659.nxt', 'caf6f9cf4909163ca37ecf1956874865', 0), -(283, 14, 3659, 'lua_0.3659.nxt', '5f557facb06c9006126b5be2a4881798', 0), -(284, 15, 3659, 'meshes_0.3659.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(285, 17, 3659, 'modules_0.3659.nxt', '89cc1c9c5f6a99a59966aaf30090bea6', 0), -(286, 18, 3659, 'projectiles_0.3659.nxt', '0ba261e8f444dfe7f4e12e748e5f159c', 0), -(287, 19, 3659, 'schook_0.3659.nxt', 'b9b6fe2c89d614d402a0335cf3e2fcba', 0), -(288, 20, 3659, 'textures_0.3659.nxt', 'f275298accf9552a6604ed62306add72', 0), -(289, 21, 3659, 'units_0.3659.nxt', '524b476ad5f64e36d9ddfe0a91a2c932', 0), -(290, 22, 3659, 'etc_0.3659.nxt', '3514201b87653551cf5db696556ec475', 0), -(434, 11, 3660, 'effects_0.3660.nxt', 'ce6d16a8ebd7f058a27f5ab6c0787530', 0), -(435, 12, 3660, 'env_0.3660.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(436, 13, 3660, 'loc_0.3660.nxt', '576fee631af11d7d4eddc5adcfd10dfa', 0), -(437, 14, 3660, 'lua_0.3660.nxt', '31a6f3014812c06f8979bce1e0903b4e', 0), -(438, 15, 3660, 'meshes_0.3660.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(439, 17, 3660, 'modules_0.3660.nxt', 'd39bb3e29bcc1fef41c0e47a705b6bb9', 0), -(440, 18, 3660, 'projectiles_0.3660.nxt', '28b6453e1e432985be201fd7bdde4250', 0), -(441, 19, 3660, 'schook_0.3660.nxt', 'a51b4f97dbdec1ebb7151f47340a0baa', 0), -(442, 20, 3660, 'textures_0.3660.nxt', '87e3ff26c66d3792c0b51b67c2c6487f', 0), -(443, 21, 3660, 'units_0.3660.nxt', '1ab896098a9b8206b5a15007ca7a5b98', 0), -(444, 22, 3660, 'etc_0.3660.nxt', '3514201b87653551cf5db696556ec475', 0), -(467, 11, 3662, 'effects_0.3662.nxt', 'ce6d16a8ebd7f058a27f5ab6c0787530', 0), -(468, 12, 3662, 'env_0.3662.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(469, 13, 3662, 'loc_0.3662.nxt', 'e539fff338506a2dd60383814ba06ade', 0), -(470, 14, 3662, 'lua_0.3662.nxt', 'e182bfcba5f074e64740a45dff354013', 0), -(471, 15, 3662, 'meshes_0.3662.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(472, 17, 3662, 'modules_0.3662.nxt', 'd39bb3e29bcc1fef41c0e47a705b6bb9', 0), -(473, 18, 3662, 'projectiles_0.3662.nxt', '0ba261e8f444dfe7f4e12e748e5f159c', 0), -(474, 19, 3662, 'schook_0.3662.nxt', 'a51b4f97dbdec1ebb7151f47340a0baa', 0), -(475, 20, 3662, 'textures_0.3662.nxt', '87e3ff26c66d3792c0b51b67c2c6487f', 0), -(476, 21, 3662, 'units_0.3662.nxt', '6610ef6f832d8f5361053d239aeaec10', 0), -(477, 22, 3662, 'etc_0.3662.nxt', '5d334af582b719f05c963635e1f524df', 0), -(498, 11, 3663, 'effects_0.3663.nxt', 'ce6d16a8ebd7f058a27f5ab6c0787530', 0), -(499, 12, 3663, 'env_0.3663.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(500, 13, 3663, 'loc_0.3663.nxt', 'e539fff338506a2dd60383814ba06ade', 0), -(501, 14, 3663, 'lua_0.3663.nxt', '2f4e4a48f3bead03d698484fac1ae412', 0), -(502, 15, 3663, 'meshes_0.3663.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(503, 17, 3663, 'modules_0.3663.nxt', 'd39bb3e29bcc1fef41c0e47a705b6bb9', 0), -(504, 18, 3663, 'projectiles_0.3663.nxt', '0ba261e8f444dfe7f4e12e748e5f159c', 0), -(505, 19, 3663, 'schook_0.3663.nxt', 'a51b4f97dbdec1ebb7151f47340a0baa', 0), -(506, 20, 3663, 'textures_0.3663.nxt', '87e3ff26c66d3792c0b51b67c2c6487f', 0), -(507, 21, 3663, 'units_0.3663.nxt', 'e9968b6985b1197c6df3d0d8ad03b2d6', 0), -(508, 22, 3663, 'etc_0.3663.nxt', '5d334af582b719f05c963635e1f524df', 0), -(509, 1, 3663, 'ForgedAlliance.3663.exe', '647693d2c680ff5177741aeeebbe9b5b', 0), -(510, 11, 3664, 'effects_0.3664.nxt', 'ce6d16a8ebd7f058a27f5ab6c0787530', 0), -(511, 12, 3664, 'env_0.3664.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(512, 13, 3664, 'loc_0.3664.nxt', '9e9fb4703603e3661161f291a4c77134', 0), -(513, 14, 3664, 'lua_0.3664.nxt', '6087597c9f71afd23dec5dd4bd1a1eeb', 0), -(514, 15, 3664, 'meshes_0.3664.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(515, 17, 3664, 'modules_0.3664.nxt', 'd39bb3e29bcc1fef41c0e47a705b6bb9', 0), -(516, 18, 3664, 'projectiles_0.3664.nxt', '28b6453e1e432985be201fd7bdde4250', 0), -(517, 19, 3664, 'schook_0.3664.nxt', 'a51b4f97dbdec1ebb7151f47340a0baa', 0), -(518, 20, 3664, 'textures_0.3664.nxt', '87e3ff26c66d3792c0b51b67c2c6487f', 0), -(519, 21, 3664, 'units_0.3664.nxt', 'bd46fa7db35df0d2602af6da599c8b27', 0), -(520, 22, 3664, 'etc_0.3664.nxt', '5d334af582b719f05c963635e1f524df', 0), -(521, 1, 3664, 'ForgedAlliance.3664.exe', '37d717431bcd8549cebb74541ddb64fa', 0), -(522, 11, 3665, 'effects_0.3665.nxt', 'ce6d16a8ebd7f058a27f5ab6c0787530', 0), -(523, 12, 3665, 'env_0.3665.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(524, 13, 3665, 'loc_0.3665.nxt', '9e9fb4703603e3661161f291a4c77134', 0), -(525, 14, 3665, 'lua_0.3665.nxt', '6087597c9f71afd23dec5dd4bd1a1eeb', 0), -(526, 15, 3665, 'meshes_0.3665.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(527, 17, 3665, 'modules_0.3665.nxt', 'd39bb3e29bcc1fef41c0e47a705b6bb9', 0), -(528, 18, 3665, 'projectiles_0.3665.nxt', '28b6453e1e432985be201fd7bdde4250', 0), -(529, 19, 3665, 'schook_0.3665.nxt', 'a51b4f97dbdec1ebb7151f47340a0baa', 0), -(530, 20, 3665, 'textures_0.3665.nxt', '87e3ff26c66d3792c0b51b67c2c6487f', 0), -(531, 21, 3665, 'units_0.3665.nxt', 'bd46fa7db35df0d2602af6da599c8b27', 0), -(532, 22, 3665, 'etc_0.3665.nxt', '5d334af582b719f05c963635e1f524df', 0), -(533, 1, 3665, 'ForgedAlliance.3665.exe', 'f7b6ce9f2decccc86462bc971df6daf1', 0), -(537, 11, 3666, 'effects_0.3666.nxt', '4515b175e2b827131871ccbd72016c76', 0), -(538, 12, 3666, 'env_0.3666.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(539, 13, 3666, 'loc_0.3666.nxt', 'bd29abc6e7370990be1697ff4545c7df', 0), -(540, 14, 3666, 'lua_0.3666.nxt', 'fb8ef62f0bf70b06edf020c68150ce26', 0), -(541, 15, 3666, 'meshes_0.3666.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(542, 17, 3666, 'modules_0.3666.nxt', 'ef0d9fba50c0adaa1e4615c5fcf3c309', 0), -(543, 18, 3666, 'projectiles_0.3666.nxt', '0ba261e8f444dfe7f4e12e748e5f159c', 0), -(544, 19, 3666, 'schook_0.3666.nxt', 'ce96f20d4bf19d8a231e97ac86e674fa', 0), -(545, 20, 3666, 'textures_0.3666.nxt', 'c6ad21d72c6c937d23a35af4eda78dc4', 0), -(546, 21, 3666, 'units_0.3666.nxt', '6383db6ee0f463dd340972d44e3ad7fa', 0), -(547, 22, 3666, 'etc_0.3666.nxt', '0903a5a32cb70c564349dcfe84fba9a7', 0), -(548, 1, 3666, 'ForgedAlliance.3666.exe', '8036f2d3f3d0670e96822bc4e2ae6d4d', 0), -(549, 11, 3667, 'effects_0.3667.nxt', '4515b175e2b827131871ccbd72016c76', 0), -(550, 12, 3667, 'env_0.3667.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(551, 13, 3667, 'loc_0.3667.nxt', 'bd29abc6e7370990be1697ff4545c7df', 0), -(552, 14, 3667, 'lua_0.3667.nxt', 'fb8ef62f0bf70b06edf020c68150ce26', 0), -(553, 15, 3667, 'meshes_0.3667.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(554, 17, 3667, 'modules_0.3667.nxt', 'ef0d9fba50c0adaa1e4615c5fcf3c309', 0), -(555, 18, 3667, 'projectiles_0.3667.nxt', '0ba261e8f444dfe7f4e12e748e5f159c', 0), -(556, 19, 3667, 'schook_0.3667.nxt', 'ce96f20d4bf19d8a231e97ac86e674fa', 0), -(557, 20, 3667, 'textures_0.3667.nxt', 'c6ad21d72c6c937d23a35af4eda78dc4', 0), -(558, 21, 3667, 'units_0.3667.nxt', '6383db6ee0f463dd340972d44e3ad7fa', 0), -(559, 22, 3667, 'etc_0.3667.nxt', '0903a5a32cb70c564349dcfe84fba9a7', 0), -(560, 1, 3667, 'ForgedAlliance.3667.exe', 'de8492bf49b6a980f5745db6892e3884', 0), -(561, 11, 3672, 'effects_0.3672.nxt', '4515b175e2b827131871ccbd72016c76', 0), -(562, 12, 3672, 'env_0.3672.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(563, 13, 3672, 'loc_0.3672.nxt', 'bd29abc6e7370990be1697ff4545c7df', 0), -(564, 14, 3672, 'lua_0.3672.nxt', '47817bab524c17ce77176aa24eda7fe8', 0), -(565, 15, 3672, 'meshes_0.3672.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(566, 17, 3672, 'modules_0.3672.nxt', 'ef0d9fba50c0adaa1e4615c5fcf3c309', 0), -(567, 18, 3672, 'projectiles_0.3672.nxt', '28b6453e1e432985be201fd7bdde4250', 0), -(568, 19, 3672, 'schook_0.3672.nxt', 'ce96f20d4bf19d8a231e97ac86e674fa', 0), -(569, 20, 3672, 'textures_0.3672.nxt', 'c6ad21d72c6c937d23a35af4eda78dc4', 0), -(570, 21, 3672, 'units_0.3672.nxt', '0d8e9aa06471a3d12fd1acac54343040', 0), -(571, 22, 3672, 'etc_0.3672.nxt', '0903a5a32cb70c564349dcfe84fba9a7', 0), -(572, 1, 3672, 'ForgedAlliance.3672.exe', '6f60f0fae2ffa287c623d90743aa4ab4', 0), -(573, 11, 3675, 'effects_0.3675.nxt', '4515b175e2b827131871ccbd72016c76', 0), -(574, 12, 3675, 'env_0.3675.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(575, 13, 3675, 'loc_0.3675.nxt', 'f6fe4ab343455c24248d67ead54dee33', 0), -(576, 14, 3675, 'lua_0.3675.nxt', '9f0a8b4a8b3562ca97f75ae8cd94ccee', 0), -(577, 15, 3675, 'meshes_0.3675.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(578, 17, 3675, 'modules_0.3675.nxt', '4c91eadddbf40122f8cd09283fdc1683', 0), -(579, 18, 3675, 'projectiles_0.3675.nxt', 'e2647c7ff11599d22c568c0cd1248d89', 0), -(580, 19, 3675, 'schook_0.3675.nxt', 'ce96f20d4bf19d8a231e97ac86e674fa', 0), -(581, 20, 3675, 'textures_0.3675.nxt', 'c6ad21d72c6c937d23a35af4eda78dc4', 0), -(582, 21, 3675, 'units_0.3675.nxt', '88a9f1cec5d00a446068ecda94f360ce', 0), -(583, 22, 3675, 'etc_0.3675.nxt', '2bf5a88f0cd370dcc037a96627ecc81e', 0), -(584, 1, 3675, 'ForgedAlliance.3675.exe', '54ad49b7838abfc1d498783614a0176a', 0), -(633, 11, 3676, 'effects_0.3676.nxt', '4515b175e2b827131871ccbd72016c76', 0), -(634, 12, 3676, 'env_0.3676.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(635, 13, 3676, 'loc_0.3676.nxt', '13df257b7367637c14082c9906a16408', 0), -(636, 14, 3676, 'lua_0.3676.nxt', '3215a1392a0ef76ce7b4ed454b59594c', 0), -(637, 15, 3676, 'meshes_0.3676.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(638, 17, 3676, 'modules_0.3676.nxt', 'a38366bd1e20c21ea0a92f3c8fde5d97', 0), -(639, 18, 3676, 'projectiles_0.3676.nxt', '28b6453e1e432985be201fd7bdde4250', 0), -(640, 19, 3676, 'schook_0.3676.nxt', 'ce96f20d4bf19d8a231e97ac86e674fa', 0), -(641, 20, 3676, 'textures_0.3676.nxt', '5ae4cbd544204a93a5c03eec2a4b7ff6', 0), -(642, 21, 3676, 'units_0.3676.nxt', 'f3b20444f90c0c423a89ab11eaf24e16', 0), -(643, 22, 3676, 'etc_0.3676.nxt', '6f720d8261abf03f6c77f1971e5be04f', 0), -(644, 1, 3676, 'ForgedAlliance.3676.exe', 'cb9d3ef03be5800ce761c9e4914d9b86', 0), -(801, 22, 3678, 'etc.3678.nxt', 'b918ac7a67d049d5a76d04e059c3015d', 0), -(802, 12, 3678, 'env.3678.nxt', 'bb1644d1ba5baa07d63395ce76315d81', 0), -(803, 17, 3678, 'modules.3678.nxt', 'e6c6cd72f3f8740527cf8889622f5c18', 0), -(804, 11, 3678, 'effects.3678.nxt', '35a6f614efcc554b0eb35ecd551a944f', 0), -(805, 21, 3678, 'units.3678.nxt', 'a1fda89aba07a5187364ef17fb954241', 0), -(806, 15, 3678, 'meshes.3678.nxt', '164be3f0c7c256e5eee37248d87f7342', 0), -(807, 18, 3678, 'projectiles.3678.nxt', '4bc9d87157859bbded8b1a9ec070ba3c', 0), -(808, 19, 3678, 'schook.3678.nxt', '7cea700765db6332db24bc2a4c381e62', 0), -(809, 14, 3678, 'lua.3678.nxt', '3203164a25199be4be811a47ded056b8', 0), -(810, 13, 3678, 'loc.3678.nxt', '6f9f332721b848258740350f80513503', 0), -(811, 20, 3678, 'textures.3678.nxt', '3a2fa4f900f2abab3ef932f4d2679fa6', 0), -(812, 1, 3678, 'ForgedAlliance.3678.exe', 'c8e6afa7799743ef1d32b6e2fea7cfca', 0), -(868, 22, 3682, 'etc.3682.nx4', 'fe9fb46249ce718e21fe5cb1c85e646e', 0), -(869, 12, 3682, 'env.3682.nx4', 'f6724caf818c4acbb69f4f060f6a273b', 0), -(870, 11, 3682, 'effects.3682.nx4', 'd07346f3328e28e3a808aa63a7d5eb26', 0), -(871, 21, 3682, 'units.3682.nx4', 'a31febda4db2a5774cf1815babda161f', 0), -(872, 15, 3682, 'meshes.3682.nx4', 'ed02aec5422e6f5e54bd1f807ed86acb', 0), -(873, 18, 3682, 'projectiles.3682.nx4', '03bf9dc392443d275daccf41c834a09a', 0), -(874, 19, 3682, 'schook.3682.nx4', '588e206656ef632c59c8687ff20046d1', 0), -(875, 14, 3682, 'lua.3682.nx4', '9fa642d450431ec5592d07ee19894cbf', 0), -(876, 13, 3682, 'loc.3682.nx4', '0840afc4d84e5a56d7cd8e376af448b5', 0), -(877, 20, 3682, 'textures.3682.nx4', 'bb89ed7b678e1f6a4be672753e00939b', 0), -(878, 1, 3682, 'ForgedAlliance.3682.exe', '334e86d0e0531e3539b1e3c8111e452c', 0), -(901, 22, 3684, 'etc.3684.nx4', 'ac54bf8d21ecd70d507fce4d11bb1fb3', 0), -(902, 12, 3684, 'env.3684.nx4', 'c1c3bd39895c2a84eccf9863bc7fd530', 0), -(903, 11, 3684, 'effects.3684.nx4', '97612e56f7fd98cc87ebb43b0e2b2e19', 0), -(904, 21, 3684, 'units.3684.nx4', '42d1406e905d786eb7a1dbfbf008f2b2', 0), -(905, 15, 3684, 'meshes.3684.nx4', 'bc6e29201df7203c0d73f51f2eb0f19a', 0), -(906, 18, 3684, 'projectiles.3684.nx4', 'd0254c186be7e787aef4ef13425675e0', 0), -(907, 19, 3684, 'schook.3684.nx4', '48395afd35915435f8b9487107b269e5', 0), -(908, 14, 3684, 'lua.3684.nx4', 'e91fccccfe88710eed13de90ee0d0170', 0), -(909, 13, 3684, 'loc.3684.nx4', 'f9b2f1d42c647600139b8f999a1f0587', 0), -(910, 20, 3684, 'textures.3684.nx4', 'a54956ff44be6f8511bdbe2186674bfe', 0), -(911, 1, 3684, 'ForgedAlliance.3684.exe', '0f9e085d44da00e0ce314c181cf087a8', 0), -(978, 22, 3686, 'etc.3686.nx4', 'b739a7cfa4a6eb04b8e7f3863d1e568e', 0), -(979, 12, 3686, 'env.3686.nx4', 'abb0a29cc31865fb2ab5110940a62b6a', 0), -(980, 11, 3686, 'effects.3686.nx4', 'de42aa8870c23cd991f6350400e241ce', 0), -(981, 21, 3686, 'units.3686.nx4', '0d9448fcba34171f187d820fe7091bc2', 0), -(982, 15, 3686, 'meshes.3686.nx4', 'ffcb971fe8db79495eeb19b746f6480b', 0), -(983, 18, 3686, 'projectiles.3686.nx4', '2cfba94b9710d2f0ca428fd8d9faf6d2', 0), -(984, 19, 3686, 'schook.3686.nx4', '2c4470dd098cbbbce6610ba2c33ec43b', 0), -(985, 14, 3686, 'lua.3686.nx4', '8417b039fd7414dc1dfb579f8ba195a1', 0), -(986, 13, 3686, 'loc.3686.nx4', '6d5ad58090696a79a778c3c335ed8b6e', 0), -(987, 20, 3686, 'textures.3686.nx4', 'ccb2783e34b8d790da4c8625f4477a44', 0), -(988, 1, 3686, 'ForgedAlliance.3686.exe', '4d939f813fcf987435669719e703ca89', 0), -(1011, 12, 3689, 'env.3689.nx4', 'cd2d44a798c1608b5164f9565fbd0052', 0), -(1012, 15, 3689, 'meshes.3689.nx4', '3c9048754067ce46e0ffe6e6d80d1b9e', 0), -(1013, 19, 3689, 'schook.3689.nx4', 'aba8a4e936101628c802f8c429337d0f', 0), -(1014, 14, 3689, 'lua.3689.nx4', '85c17df93d5fbdad59d28f574502ae11', 0), -(1015, 22, 3689, 'etc.3689.nx4', '28935ce19a8c58af10c4bdff7901bd03', 0), -(1016, 11, 3689, 'effects.3689.nx4', '24b74da32c034660d40e86024de629d9', 0), -(1017, 20, 3689, 'textures.3689.nx4', '12eb315add65298044b964cec3e7c5cd', 0), -(1018, 18, 3689, 'projectiles.3689.nx4', 'e80f607016aa70fd6d97be550aae6cdb', 0), -(1019, 13, 3689, 'loc.3689.nx4', '2463fc1010c18d893b48eef95dce9180', 0), -(1020, 21, 3689, 'units.3689.nx4', '3fce3ca6df0fe97603a3656eaaec3d9e', 0), -(1021, 1, 3689, 'ForgedAlliance.3689.exe', '040147d8af136d4202fbda55542c9f2e', 0), -(1110, 12, 3695, 'env.3695.nx4', '57fc03a5506012143b0cb69d11634292', 0), -(1111, 15, 3695, 'meshes.3695.nx4', '49b9db1cc6241536da17fe22564986e5', 0), -(1112, 19, 3695, 'schook.3695.nx4', 'fec8dc5bc9c389ce05de0ea8cab070dd', 0), -(1113, 14, 3695, 'lua.3695.nx4', 'd646596d272198d09bd55d9a90094c25', 0), -(1114, 22, 3695, 'etc.3695.nx4', 'ba7c51bfb81e4e76ff72a822954c9e55', 0), -(1115, 11, 3695, 'effects.3695.nx4', 'd7b619f97c58f1233fa97eebf97dd528', 0), -(1116, 20, 3695, 'textures.3695.nx4', '22f7140bed0250b6a83f0c1040cc0d16', 0), -(1117, 18, 3695, 'projectiles.3695.nx4', 'ec5a0cc9130bcc642657ef9fccca0c49', 0), -(1118, 13, 3695, 'loc.3695.nx4', 'cc4282b2a22f82e77c00eff1dfe7cab8', 0), -(1119, 21, 3695, 'units.3695.nx4', '84dae2a7425f1fdfbf7efecb6c14bfb5', 0), -(1120, 1, 3695, 'ForgedAlliance.3695.exe', 'd8b89bd62950a6a2086b54a2370475e4', 0), -(1132, 12, 3696, 'env.3696.nx4', '1f22fe7781db0b9a1bdf2006e3bc3669', 0), -(1133, 15, 3696, 'meshes.3696.nx4', 'c0c9f7b884eaa080b3609199ca97fa6b', 0), -(1134, 19, 3696, 'schook.3696.nx4', 'f26381597b89f2b99d197ef161075948', 0), -(1135, 14, 3696, 'lua.3696.nx4', 'b5bfe3a95d795f8abc371af1aca70b9d', 0), -(1136, 22, 3696, 'etc.3696.nx4', '9c11ee07d979555b0bfd67c5ccbc9418', 0), -(1137, 11, 3696, 'effects.3696.nx4', '425cef6410d952f06295cec4e14b9882', 0), -(1138, 20, 3696, 'textures.3696.nx4', 'e6fad37a67ba4da7862ae1606ed02267', 0), -(1139, 18, 3696, 'projectiles.3696.nx4', '9e2128da5d631a55ad2a61b553e03025', 0), -(1140, 13, 3696, 'loc.3696.nx4', '7856ca3cf7582a9921e9edc81d892e7f', 0), -(1141, 21, 3696, 'units.3696.nx4', '6edb2aef5a64e479cd0db4bd421462b0', 0), -(1142, 1, 3696, 'ForgedAlliance.3696.exe', 'd95f9d1d70f0fc941f8f5c90c55e2a94', 0), -(1275, 12, 3698, 'env.3698.nx4', '083177c34a323ce7a0208eee5f1f8e1f', 0), -(1276, 15, 3698, 'meshes.3698.nx4', 'b9476380010780e7c3ed80f9064b8d53', 0), -(1277, 19, 3698, 'schook.3698.nx4', '7cf315bfcbb7b70533811fcd2e9ea1bf', 0), -(1278, 14, 3698, 'lua.3698.nx4', 'c8a7453cc221967a252310ca788fc393', 0), -(1279, 22, 3698, 'etc.3698.nx4', '33ff960fd6247089ed3e9f18792bcd30', 0), -(1280, 11, 3698, 'effects.3698.nx4', '6a152989c8e595d782ddb9f7b7d7acdc', 0), -(1281, 20, 3698, 'textures.3698.nx4', '1f6d6ec857c3d32dc25ee0ceda61a14d', 0), -(1282, 18, 3698, 'projectiles.3698.nx4', '38b6ad1354831e927e537e0cdbea287a', 0), -(1283, 13, 3698, 'loc.3698.nx4', '842c5dee64e7a7fe0b6d041f350a973f', 0), -(1284, 21, 3698, 'units.3698.nx4', '480061965eedebbd33efaaad9a854e12', 0), -(1285, 1, 3698, 'ForgedAlliance.3698.exe', 'c0f936b1c9675f57b688ad00a5c486de', 0), -(1341, 12, 3702, 'env.3702.nx4', '9a68e7396d91319c230f13996bad7035', 0), -(1342, 15, 3702, 'meshes.3702.nx4', 'f1320982025e92a2a8e30b07e10c98e8', 0), -(1343, 19, 3702, 'schook.3702.nx4', 'd9fba5cbc485c007335e5c6632b24732', 0), -(1344, 14, 3702, 'lua.3702.nx4', '8bd63fa67251dab33cabac863adabb08', 0), -(1345, 22, 3702, 'etc.3702.nx4', 'e26b531b1dd1644928d561966f0fae7e', 0), -(1346, 11, 3702, 'effects.3702.nx4', '3061a0d6abad344bb4ae93ea8f07bfa2', 0), -(1347, 20, 3702, 'textures.3702.nx4', '72eae59751ac54225a3dc4b9aaef1db4', 0), -(1348, 18, 3702, 'projectiles.3702.nx4', 'daed2d926fbe28ea2dcd25f104d9aa49', 0), -(1349, 13, 3702, 'loc.3702.nx4', 'dfb72d07d3096f74edf7b7c328a5f089', 0), -(1350, 21, 3702, 'units.3702.nx4', 'a80d3078e5484615b0845abfc78d1afa', 0), -(1351, 1, 3702, 'ForgedAlliance.3702.exe', '819558d8cf924dce94c038882e6ed229', 0), -(1649, 12, 3703, 'env.3703.nx4', 'a7fb2a4aed335e755cf04f4437083218', 0), -(1650, 15, 3703, 'meshes.3703.nx4', 'b5fe9aad7f12b399be325325dcd295ab', 0), -(1651, 19, 3703, 'schook.3703.nx4', '0086a01969af84bd0ea21d83b9ce035c', 0), -(1652, 14, 3703, 'lua.3703.nx4', 'f87efecbea867cf574881a45ff248153', 0), -(1653, 22, 3703, 'etc.3703.nx4', '7c24e89089867fd47d18f1f1c735f2c4', 0), -(1654, 11, 3703, 'effects.3703.nx4', '2f11328e51008e87f1fa9648e1e7b9c6', 0), -(1655, 20, 3703, 'textures.3703.nx4', 'b901e8ab925c1e843b9b267a502ccbe2', 0), -(1656, 18, 3703, 'projectiles.3703.nx4', '0e8e0857f132a72d56c066060c8af267', 0), -(1657, 13, 3703, 'loc.3703.nx4', 'f3f3675b90bcaaa04841a3f6320044d2', 0), -(1658, 21, 3703, 'units.3703.nx4', '2d2f2a139f8b3c30e5b9cc19b449f5f3', 0), -(1659, 1, 3703, 'ForgedAlliance.3703.exe', '80d1355256229ab53ba6f24db1fd6a7f', 0), -(1682, 12, 3704, 'env.3704.nx4', 'f5749a9f7be116e79f826a17c418178f', 0), -(1683, 15, 3704, 'meshes.3704.nx4', '9a35fd2bc616476623fac1a87189aac0', 0), -(1684, 19, 3704, 'schook.3704.nx4', '0511820a40e11e68b104fc4189fb35d7', 0), -(1685, 14, 3704, 'lua.3704.nx4', '620bbc301aeac6d83890c353bfc3e3b2', 0), -(1686, 22, 3704, 'etc.3704.nx4', 'd22810e53e40a1f1138a6110f52b5f39', 0), -(1687, 11, 3704, 'effects.3704.nx4', '7dfb96d1447e2d1b9bc44c580457e42e', 0), -(1688, 20, 3704, 'textures.3704.nx4', 'b3b956b95fe98970670259effdebe350', 0), -(1689, 18, 3704, 'projectiles.3704.nx4', 'e9ccf0d21c6a7acda79d177638e8c59d', 0), -(1690, 13, 3704, 'loc.3704.nx4', '3df005b94b84602896cba5ef37648a25', 0), -(1691, 21, 3704, 'units.3704.nx4', '26729adb4c7211ff1dac4b29412b368b', 0), -(1692, 1, 3704, 'ForgedAlliance.3704.exe', '5ff2a30f1225d3396409661c5043c2c2', 0), -(1693, 12, 3706, 'env.3706.nx4', '8d832a91df87847dbca8b5c306795b29', 0), -(1694, 15, 3706, 'meshes.3706.nx4', 'a75e096268f58d89c452c6c63e5f33c0', 0), -(1695, 19, 3706, 'schook.3706.nx4', '52a255058a998abd9dbdeb2f52dd2309', 0), -(1696, 14, 3706, 'lua.3706.nx4', 'ff9d0ee59a2caf1c5cc98cb51aa28048', 0), -(1697, 22, 3706, 'etc.3706.nx4', '9e869dec0cb0c886500f9b255f0e67c3', 0), -(1698, 11, 3706, 'effects.3706.nx4', '342bd479532ee509bd4558cae58b0e6f', 0), -(1699, 20, 3706, 'textures.3706.nx4', 'a8209cd96c3ce18e2e09188f46fe14cd', 0), -(1700, 18, 3706, 'projectiles.3706.nx4', 'aecc33193bcd9f442d8b74aa209f221d', 0), -(1701, 13, 3706, 'loc.3706.nx4', '6993c44ceaf9f0e2520174defbd5b588', 0), -(1702, 21, 3706, 'units.3706.nx4', '848507e9f8b27d1121dc3d96a9bcce26', 0), (1703, 1, 3706, 'ForgedAlliance.3706.exe', 'c20b922a785cf5876c39b7696a16f162', 0); INSERT INTO `updates_fafdevelop` (`id`, `filename`, `path`) VALUES -(1, 'ForgedAlliance.exe', 'bin'), -(2, 'init_fafdevelop.lua', 'bin'), -(11, 'effects.nx5', 'gamedata'), -(12, 'env.nx5', 'gamedata'), -(13, 'loc.nx5', 'gamedata'), -(14, 'lua.nx5', 'gamedata'), -(15, 'meshes.nx5', 'gamedata'), -(17, 'modules.nx5', 'gamedata'), -(18, 'projectiles.nx5', 'gamedata'), -(19, 'schook.nx5', 'gamedata'), -(20, 'textures.nx5', 'gamedata'), -(21, 'units.nx5', 'gamedata'), -(22, 'etc.nx5', 'gamedata'); +(1, 'ForgedAlliance.exe', 'bin'); INSERT INTO `updates_fafdevelop_files` (`id`, `fileId`, `version`, `name`, `md5`, `obselete`) VALUES -(1, 2, 3652, 'init_fafdevelop_3658.lua', 'b701f97a451698c2caaef06dbdf72fe4', 0), -(2, 11, 3652, 'effects_0.3652.nxt', '31d289288784211fbf57fb639ce88a64', 0), -(3, 12, 3652, 'env_0.3652.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(4, 13, 3652, 'loc_0.3652.nxt', '97464f661ea84600543bbde05d12dd73', 0), -(5, 14, 3652, 'lua_0.3652.nxt', '7642fea8dfc908b490ec60db67ece06c', 0), -(6, 15, 3652, 'meshes_0.3652.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(7, 17, 3652, 'modules_0.3652.nxt', '6a81c76137fe7fe6579c55aabf03dbb7', 0), -(8, 18, 3652, 'projectiles_0.3652.nxt', 'ecbff75d5ee54dbea7baffec71a214b4', 0), -(9, 19, 3652, 'schook_0.3652.nxt', 'd70eb26a7f9eeddc28df711b839156b0', 0), -(10, 20, 3652, 'textures_0.3652.nxt', '1e0dd68c463f300234423a2346e5a979', 0), -(11, 21, 3652, 'units_0.3652.nxt', '0e91d45f764d27c7924a09384a47e60a', 0), -(12, 22, 3652, 'etc_0.3652.nxt', 'cb38d0bc92e4d01b8afce879bd3ab669', 0), -(24, 11, 3653, 'effects_0.3653.nxt', '7823e173c153ce3fc2977c68c54f1a3c', 0), -(25, 12, 3653, 'env_0.3653.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(26, 13, 3653, 'loc_0.3653.nxt', '85b23808ab6478b2fd303da8248bf738', 0), -(27, 14, 3653, 'lua_0.3653.nxt', 'f72663f46e1b15ec45314221a82b4d77', 0), -(28, 15, 3653, 'meshes_0.3653.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(29, 17, 3653, 'modules_0.3653.nxt', 'e20fec02e2251b5085cbe4096b7769a9', 0), -(30, 18, 3653, 'projectiles_0.3653.nxt', 'c1a7b415a3f5477470150e91580ccf19', 0), -(31, 19, 3653, 'schook_0.3653.nxt', 'fc661665d45af0976f5a70de863046a2', 0), -(32, 20, 3653, 'textures_0.3653.nxt', 'b72caa7d4e76ff71471badf55b8ee800', 0), -(33, 21, 3653, 'units_0.3653.nxt', '1255955a81ee7d73862ed6a54fb0d42f', 0), -(34, 22, 3653, 'etc_0.3653.nxt', '3277eb482e12fe0231fe692f356d935b', 0), -(35, 11, 3654, 'effects_0.3654.nxt', '7823e173c153ce3fc2977c68c54f1a3c', 0), -(36, 12, 3654, 'env_0.3654.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(37, 13, 3654, 'loc_0.3654.nxt', '6e9f6a5e148e94a8a292ed58d13f90d4', 0), -(38, 14, 3654, 'lua_0.3654.nxt', 'aaae2e5185b8ccb555296cb36f5af460', 0), -(39, 15, 3654, 'meshes_0.3654.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(40, 17, 3654, 'modules_0.3654.nxt', 'e20fec02e2251b5085cbe4096b7769a9', 0), -(41, 18, 3654, 'projectiles_0.3654.nxt', 'c1a7b415a3f5477470150e91580ccf19', 0), -(42, 19, 3654, 'schook_0.3654.nxt', 'fc661665d45af0976f5a70de863046a2', 0), -(43, 20, 3654, 'textures_0.3654.nxt', 'b72caa7d4e76ff71471badf55b8ee800', 0), -(44, 21, 3654, 'units_0.3654.nxt', '45e8a56d3d8f5750611961459f191c7a', 0), -(45, 22, 3654, 'etc_0.3654.nxt', '3277eb482e12fe0231fe692f356d935b', 0), -(46, 11, 3655, 'effects_0.3655.nxt', '7823e173c153ce3fc2977c68c54f1a3c', 0), -(47, 12, 3655, 'env_0.3655.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(48, 13, 3655, 'loc_0.3655.nxt', '6e9f6a5e148e94a8a292ed58d13f90d4', 0), -(49, 14, 3655, 'lua_0.3655.nxt', '5b34a8b361448cd3f515c96c4faca15b', 0), -(50, 15, 3655, 'meshes_0.3655.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(51, 17, 3655, 'modules_0.3655.nxt', 'e20fec02e2251b5085cbe4096b7769a9', 0), -(52, 18, 3655, 'projectiles_0.3655.nxt', 'c1a7b415a3f5477470150e91580ccf19', 0), -(53, 19, 3655, 'schook_0.3655.nxt', '122e008515f6a33e2d7936650373423e', 0), -(54, 20, 3655, 'textures_0.3655.nxt', 'ad4c595b8c0a0fb8b0e1e78ccf07c192', 0), -(55, 21, 3655, 'units_0.3655.nxt', '6fc20c5699bef0d18691fdf62cbb09d3', 0), -(56, 22, 3655, 'etc_0.3655.nxt', '3514201b87653551cf5db696556ec475', 0), -(57, 11, 3656, 'effects_0.3656.nxt', '7823e173c153ce3fc2977c68c54f1a3c', 0), -(58, 12, 3656, 'env_0.3656.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(59, 13, 3656, 'loc_0.3656.nxt', '670934cd424bc14e85c389c73e9f4175', 0), -(60, 14, 3656, 'lua_0.3656.nxt', '6dd341f362de25c336eaf963db1e2cad', 0), -(61, 15, 3656, 'meshes_0.3656.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(62, 17, 3656, 'modules_0.3656.nxt', 'e20fec02e2251b5085cbe4096b7769a9', 0), -(63, 18, 3656, 'projectiles_0.3656.nxt', 'c1a7b415a3f5477470150e91580ccf19', 0), -(64, 19, 3656, 'schook_0.3656.nxt', '122e008515f6a33e2d7936650373423e', 0), -(65, 20, 3656, 'textures_0.3656.nxt', 'ad4c595b8c0a0fb8b0e1e78ccf07c192', 0), -(66, 21, 3656, 'units_0.3656.nxt', 'a6413d206fc9db539bbe543adfc70b0f', 0), -(67, 22, 3656, 'etc_0.3656.nxt', '3514201b87653551cf5db696556ec475', 0), -(68, 11, 3657, 'effects_0.3657.nxt', '7823e173c153ce3fc2977c68c54f1a3c', 0), -(69, 12, 3657, 'env_0.3657.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(70, 13, 3657, 'loc_0.3657.nxt', '670934cd424bc14e85c389c73e9f4175', 0), -(71, 14, 3657, 'lua_0.3657.nxt', '225aefd5d4baa4e50c758cb06b880bb1', 0), -(72, 15, 3657, 'meshes_0.3657.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(73, 17, 3657, 'modules_0.3657.nxt', 'e20fec02e2251b5085cbe4096b7769a9', 0), -(74, 18, 3657, 'projectiles_0.3657.nxt', 'c1a7b415a3f5477470150e91580ccf19', 0), -(75, 19, 3657, 'schook_0.3657.nxt', '122e008515f6a33e2d7936650373423e', 0), -(76, 20, 3657, 'textures_0.3657.nxt', 'ad4c595b8c0a0fb8b0e1e78ccf07c192', 0), -(77, 21, 3657, 'units_0.3657.nxt', 'a6413d206fc9db539bbe543adfc70b0f', 0), -(78, 22, 3657, 'etc_0.3657.nxt', '3514201b87653551cf5db696556ec475', 0), -(79, 11, 3658, 'effects_0.3658.nxt', '7823e173c153ce3fc2977c68c54f1a3c', 0), -(80, 12, 3658, 'env_0.3658.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(81, 13, 3658, 'loc_0.3658.nxt', '670934cd424bc14e85c389c73e9f4175', 0), -(82, 14, 3658, 'lua_0.3658.nxt', 'b0f6c1a40bbc41e398ee9340ea14dd1f', 0), -(83, 15, 3658, 'meshes_0.3658.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(84, 17, 3658, 'modules_0.3658.nxt', 'e20fec02e2251b5085cbe4096b7769a9', 0), -(85, 18, 3658, 'projectiles_0.3658.nxt', 'c1a7b415a3f5477470150e91580ccf19', 0), -(86, 19, 3658, 'schook_0.3658.nxt', '122e008515f6a33e2d7936650373423e', 0), -(87, 20, 3658, 'textures_0.3658.nxt', 'ad4c595b8c0a0fb8b0e1e78ccf07c192', 0), -(88, 21, 3658, 'units_0.3658.nxt', 'a6413d206fc9db539bbe543adfc70b0f', 0), -(89, 22, 3658, 'etc_0.3658.nxt', '3514201b87653551cf5db696556ec475', 0), -(280, 11, 3659, 'effects_0.3659.nxt', '3758baad77531dd5323c766433412e91', 0), -(281, 12, 3659, 'env_0.3659.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(282, 13, 3659, 'loc_0.3659.nxt', 'caf6f9cf4909163ca37ecf1956874865', 0), -(283, 14, 3659, 'lua_0.3659.nxt', '5f557facb06c9006126b5be2a4881798', 0), -(284, 15, 3659, 'meshes_0.3659.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(285, 17, 3659, 'modules_0.3659.nxt', '89cc1c9c5f6a99a59966aaf30090bea6', 0), -(286, 18, 3659, 'projectiles_0.3659.nxt', '0ba261e8f444dfe7f4e12e748e5f159c', 0), -(287, 19, 3659, 'schook_0.3659.nxt', 'b9b6fe2c89d614d402a0335cf3e2fcba', 0), -(288, 20, 3659, 'textures_0.3659.nxt', 'f275298accf9552a6604ed62306add72', 0), -(289, 21, 3659, 'units_0.3659.nxt', '524b476ad5f64e36d9ddfe0a91a2c932', 0), -(290, 22, 3659, 'etc_0.3659.nxt', '3514201b87653551cf5db696556ec475', 0), -(810, 11, 3661, 'effects_0.3661.nxt', 'ce6d16a8ebd7f058a27f5ab6c0787530', 0), -(811, 12, 3661, 'env_0.3661.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(812, 13, 3661, 'loc_0.3661.nxt', 'ef8a035fc02a799be109741b9c411261', 0), -(813, 14, 3661, 'lua_0.3661.nxt', 'e6c58daa86a28ce1b620d8711db7bdd7', 0), -(814, 15, 3661, 'meshes_0.3661.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(815, 17, 3661, 'modules_0.3661.nxt', 'd39bb3e29bcc1fef41c0e47a705b6bb9', 0), -(816, 18, 3661, 'projectiles_0.3661.nxt', '28b6453e1e432985be201fd7bdde4250', 0), -(817, 19, 3661, 'schook_0.3661.nxt', '50304947f34ede05653867173dd78611', 0), -(818, 20, 3661, 'textures_0.3661.nxt', '76c69eb0dcc5963417539a2075feea85', 0), -(819, 21, 3661, 'units_0.3661.nxt', 'a5e8161b9deb4d7ab405da3ba527ba53', 0), -(820, 22, 3661, 'etc_0.3661.nxt', '3514201b87653551cf5db696556ec475', 0), -(821, 11, 3660, 'effects_0.3660.nxt', 'ce6d16a8ebd7f058a27f5ab6c0787530', 0), -(822, 12, 3660, 'env_0.3660.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(823, 13, 3660, 'loc_0.3660.nxt', '576fee631af11d7d4eddc5adcfd10dfa', 0), -(824, 14, 3660, 'lua_0.3660.nxt', '579315c17d249d32ea7d2ab5c208e4a1', 0), -(825, 15, 3660, 'meshes_0.3660.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(826, 17, 3660, 'modules_0.3660.nxt', 'd39bb3e29bcc1fef41c0e47a705b6bb9', 0), -(827, 18, 3660, 'projectiles_0.3660.nxt', '28b6453e1e432985be201fd7bdde4250', 0), -(828, 19, 3660, 'schook_0.3660.nxt', '99a7e3f33d013b550604d17103b2ba60', 0), -(829, 20, 3660, 'textures_0.3660.nxt', '5a2016efdeee75ff6197f659cfff1a7f', 0), -(830, 21, 3660, 'units_0.3660.nxt', '1ab896098a9b8206b5a15007ca7a5b98', 0), -(831, 22, 3660, 'etc_0.3660.nxt', '3514201b87653551cf5db696556ec475', 0), -(1026, 11, 3663, 'effects_0.3663.nxt', '4515b175e2b827131871ccbd72016c76', 0), -(1027, 12, 3663, 'env_0.3663.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(1028, 13, 3663, 'loc_0.3663.nxt', 'bd29abc6e7370990be1697ff4545c7df', 0), -(1029, 14, 3663, 'lua_0.3663.nxt', 'd4c5085046ed2d970ebb4145a3cf3366', 0), -(1030, 15, 3663, 'meshes_0.3663.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(1031, 17, 3663, 'modules_0.3663.nxt', '03b8425c87246120dfa92e394149ec45', 0), -(1032, 18, 3663, 'projectiles_0.3663.nxt', '28b6453e1e432985be201fd7bdde4250', 0), -(1033, 19, 3663, 'schook_0.3663.nxt', 'ce96f20d4bf19d8a231e97ac86e674fa', 0), -(1034, 20, 3663, 'textures_0.3663.nxt', '449c065dce0cb700b47bf50911d8b299', 0), -(1035, 21, 3663, 'units_0.3663.nxt', '2b852f5eda0d6a68903061869610885f', 0), -(1036, 22, 3663, 'etc_0.3663.nxt', '6dc10971d7ca2ada4a9f817a22370db0', 0), -(1037, 1, 3663, 'ForgedAlliance.3663.exe', '647693d2c680ff5177741aeeebbe9b5b', 0), -(1050, 11, 3664, 'effects_0.3664.nxt', '4515b175e2b827131871ccbd72016c76', 0), -(1051, 12, 3664, 'env_0.3664.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(1052, 13, 3664, 'loc_0.3664.nxt', 'bd29abc6e7370990be1697ff4545c7df', 0), -(1053, 14, 3664, 'lua_0.3664.nxt', 'd4c5085046ed2d970ebb4145a3cf3366', 0), -(1054, 15, 3664, 'meshes_0.3664.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(1055, 17, 3664, 'modules_0.3664.nxt', '03b8425c87246120dfa92e394149ec45', 0), -(1056, 18, 3664, 'projectiles_0.3664.nxt', '28b6453e1e432985be201fd7bdde4250', 0), -(1057, 19, 3664, 'schook_0.3664.nxt', 'ce96f20d4bf19d8a231e97ac86e674fa', 0), -(1058, 20, 3664, 'textures_0.3664.nxt', '449c065dce0cb700b47bf50911d8b299', 0), -(1059, 21, 3664, 'units_0.3664.nxt', '2b852f5eda0d6a68903061869610885f', 0), -(1060, 22, 3664, 'etc_0.3664.nxt', '6dc10971d7ca2ada4a9f817a22370db0', 0), -(1061, 1, 3664, 'ForgedAlliance.3664.exe', '37d717431bcd8549cebb74541ddb64fa', 0), -(1090, 11, 3667, 'effects_0.3667.nxt', '4515b175e2b827131871ccbd72016c76', 0), -(1091, 12, 3667, 'env_0.3667.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(1092, 13, 3667, 'loc_0.3667.nxt', 'bd29abc6e7370990be1697ff4545c7df', 0), -(1093, 14, 3667, 'lua_0.3667.nxt', 'cd78a834e1293b9ddd90522b042850dd', 0), -(1094, 15, 3667, 'meshes_0.3667.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(1095, 17, 3667, 'modules_0.3667.nxt', 'ef0d9fba50c0adaa1e4615c5fcf3c309', 0), -(1096, 18, 3667, 'projectiles_0.3667.nxt', '28b6453e1e432985be201fd7bdde4250', 0), -(1097, 19, 3667, 'schook_0.3667.nxt', 'ce96f20d4bf19d8a231e97ac86e674fa', 0), -(1098, 20, 3667, 'textures_0.3667.nxt', '449c065dce0cb700b47bf50911d8b299', 0), -(1099, 21, 3667, 'units_0.3667.nxt', '2b852f5eda0d6a68903061869610885f', 0), -(1100, 22, 3667, 'etc_0.3667.nxt', '0903a5a32cb70c564349dcfe84fba9a7', 0), -(1101, 1, 3667, 'ForgedAlliance.3667.exe', 'de8492bf49b6a980f5745db6892e3884', 0), -(1102, 11, 3668, 'effects_0.3668.nxt', '4515b175e2b827131871ccbd72016c76', 0), -(1103, 12, 3668, 'env_0.3668.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(1104, 13, 3668, 'loc_0.3668.nxt', 'bd29abc6e7370990be1697ff4545c7df', 0), -(1105, 14, 3668, 'lua_0.3668.nxt', 'cd78a834e1293b9ddd90522b042850dd', 0), -(1106, 15, 3668, 'meshes_0.3668.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(1107, 17, 3668, 'modules_0.3668.nxt', 'ef0d9fba50c0adaa1e4615c5fcf3c309', 0), -(1108, 18, 3668, 'projectiles_0.3668.nxt', '28b6453e1e432985be201fd7bdde4250', 0), -(1109, 19, 3668, 'schook_0.3668.nxt', 'ce96f20d4bf19d8a231e97ac86e674fa', 0), -(1110, 20, 3668, 'textures_0.3668.nxt', '449c065dce0cb700b47bf50911d8b299', 0), -(1111, 21, 3668, 'units_0.3668.nxt', '2b852f5eda0d6a68903061869610885f', 0), -(1112, 22, 3668, 'etc_0.3668.nxt', '0903a5a32cb70c564349dcfe84fba9a7', 0), -(1113, 1, 3668, 'ForgedAlliance.3668.exe', '43bf2699dfe2150b0215489e536b353e', 0), -(1122, 11, 3669, 'effects_0.3669.nxt', '4515b175e2b827131871ccbd72016c76', 0), -(1123, 12, 3669, 'env_0.3669.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(1124, 13, 3669, 'loc_0.3669.nxt', 'bd29abc6e7370990be1697ff4545c7df', 0), -(1125, 14, 3669, 'lua_0.3669.nxt', 'c2e23358be9c3cd71521cfe752fb6171', 0), -(1126, 15, 3669, 'meshes_0.3669.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(1127, 17, 3669, 'modules_0.3669.nxt', 'ef0d9fba50c0adaa1e4615c5fcf3c309', 0), -(1128, 18, 3669, 'projectiles_0.3669.nxt', '28b6453e1e432985be201fd7bdde4250', 0), -(1129, 19, 3669, 'schook_0.3669.nxt', 'ce96f20d4bf19d8a231e97ac86e674fa', 0), -(1130, 20, 3669, 'textures_0.3669.nxt', '449c065dce0cb700b47bf50911d8b299', 0), -(1131, 21, 3669, 'units_0.3669.nxt', '2b852f5eda0d6a68903061869610885f', 0), -(1132, 22, 3669, 'etc_0.3669.nxt', '0903a5a32cb70c564349dcfe84fba9a7', 0), -(1133, 1, 3669, 'ForgedAlliance.3669.exe', '67ffb1f83164df80153d7ac52ba373fb', 0), -(1158, 11, 3670, 'effects_0.3670.nxt', '4515b175e2b827131871ccbd72016c76', 0), -(1159, 12, 3670, 'env_0.3670.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(1160, 13, 3670, 'loc_0.3670.nxt', 'f6fe4ab343455c24248d67ead54dee33', 0), -(1161, 14, 3670, 'lua_0.3670.nxt', 'cc6c773bbce8e4e43e3ea8a3cb752a79', 0), -(1162, 15, 3670, 'meshes_0.3670.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(1163, 17, 3670, 'modules_0.3670.nxt', 'ef0d9fba50c0adaa1e4615c5fcf3c309', 0), -(1164, 18, 3670, 'projectiles_0.3670.nxt', 'fa77241c15b7508f853d8df7ea1702b7', 0), -(1165, 19, 3670, 'schook_0.3670.nxt', 'ce96f20d4bf19d8a231e97ac86e674fa', 0), -(1166, 20, 3670, 'textures_0.3670.nxt', '449c065dce0cb700b47bf50911d8b299', 0), -(1167, 21, 3670, 'units_0.3670.nxt', '58fbdedc6ed13a50ee59c3ba1469a697', 0), -(1168, 22, 3670, 'etc_0.3670.nxt', '7641a54879197b9bf372e863e20a8d19', 0), -(1169, 1, 3670, 'ForgedAlliance.3670.exe', '653dabf8e7b1acc87ea0e719436392c1', 0), -(1182, 11, 3671, 'effects_0.3671.nxt', '4515b175e2b827131871ccbd72016c76', 0), -(1183, 12, 3671, 'env_0.3671.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(1184, 13, 3671, 'loc_0.3671.nxt', 'bd29abc6e7370990be1697ff4545c7df', 0), -(1185, 14, 3671, 'lua_0.3671.nxt', '0590693f1d78d4c23daf4ea177dd8354', 0), -(1186, 15, 3671, 'meshes_0.3671.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(1187, 17, 3671, 'modules_0.3671.nxt', 'ef0d9fba50c0adaa1e4615c5fcf3c309', 0), -(1188, 18, 3671, 'projectiles_0.3671.nxt', '28b6453e1e432985be201fd7bdde4250', 0), -(1189, 19, 3671, 'schook_0.3671.nxt', 'ce96f20d4bf19d8a231e97ac86e674fa', 0), -(1190, 20, 3671, 'textures_0.3671.nxt', '449c065dce0cb700b47bf50911d8b299', 0), -(1191, 21, 3671, 'units_0.3671.nxt', '2b852f5eda0d6a68903061869610885f', 0), -(1192, 22, 3671, 'etc_0.3671.nxt', '0903a5a32cb70c564349dcfe84fba9a7', 0), -(1193, 1, 3671, 'ForgedAlliance.3671.exe', '073e3e613fe7df2826223c2ba5aa0f50', 0), -(1230, 11, 3672, 'effects_0.3672.nxt', '4515b175e2b827131871ccbd72016c76', 0), -(1231, 12, 3672, 'env_0.3672.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(1232, 13, 3672, 'loc_0.3672.nxt', 'f6fe4ab343455c24248d67ead54dee33', 0), -(1233, 14, 3672, 'lua_0.3672.nxt', '6915e88bf893fe3b7df42310809019cb', 0), -(1234, 15, 3672, 'meshes_0.3672.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(1235, 17, 3672, 'modules_0.3672.nxt', '4c91eadddbf40122f8cd09283fdc1683', 0), -(1236, 18, 3672, 'projectiles_0.3672.nxt', '67f0187848d02e26f771339dc9cbf2d4', 0), -(1237, 19, 3672, 'schook_0.3672.nxt', 'ce96f20d4bf19d8a231e97ac86e674fa', 0), -(1238, 20, 3672, 'textures_0.3672.nxt', '449c065dce0cb700b47bf50911d8b299', 0), -(1239, 21, 3672, 'units_0.3672.nxt', '9c650108c589ff7124279af231a1aa87', 0), -(1240, 22, 3672, 'etc_0.3672.nxt', '2bf5a88f0cd370dcc037a96627ecc81e', 0), -(1241, 1, 3672, 'ForgedAlliance.3672.exe', '6f60f0fae2ffa287c623d90743aa4ab4', 0), -(1242, 11, 3674, 'effects_0.3674.nxt', '4515b175e2b827131871ccbd72016c76', 0), -(1243, 12, 3674, 'env_0.3674.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(1244, 13, 3674, 'loc_0.3674.nxt', 'f6fe4ab343455c24248d67ead54dee33', 0), -(1245, 14, 3674, 'lua_0.3674.nxt', 'ff3f0dd4ea20d0f11f4c4370317eef33', 0), -(1246, 15, 3674, 'meshes_0.3674.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(1247, 17, 3674, 'modules_0.3674.nxt', '4c91eadddbf40122f8cd09283fdc1683', 0), -(1248, 18, 3674, 'projectiles_0.3674.nxt', '67f0187848d02e26f771339dc9cbf2d4', 0), -(1249, 19, 3674, 'schook_0.3674.nxt', 'ce96f20d4bf19d8a231e97ac86e674fa', 0), -(1250, 20, 3674, 'textures_0.3674.nxt', '449c065dce0cb700b47bf50911d8b299', 0), -(1251, 21, 3674, 'units_0.3674.nxt', '6645f72a57421d521bd80211d8fc79c9', 0), -(1252, 22, 3674, 'etc_0.3674.nxt', '2bf5a88f0cd370dcc037a96627ecc81e', 0), -(1253, 1, 3674, 'ForgedAlliance.3674.exe', '5f728e146a3813500dbf17fda88f1557', 0), -(1302, 11, 3675, 'effects_0.3675.nxt', '4515b175e2b827131871ccbd72016c76', 0), -(1303, 12, 3675, 'env_0.3675.nxt', '32a50729cb5155ec679771f38a151d29', 0), -(1304, 13, 3675, 'loc_0.3675.nxt', '4162370deb8d92424c43d844ebc59865', 0), -(1305, 14, 3675, 'lua_0.3675.nxt', '0d19d333b290b5ba130a13cc421e5228', 0), -(1306, 15, 3675, 'meshes_0.3675.nxt', '200972ee316ed09371dc9f357505bd66', 0), -(1307, 17, 3675, 'modules_0.3675.nxt', '845ac769d62457e7c29f478cb954041a', 0), -(1308, 18, 3675, 'projectiles_0.3675.nxt', '2381191f3caa384164ed457ac51b5a32', 0), -(1309, 19, 3675, 'schook_0.3675.nxt', 'ce96f20d4bf19d8a231e97ac86e674fa', 0), -(1310, 20, 3675, 'textures_0.3675.nxt', '68e175be351588751a33b88c3d0f533d', 0), -(1311, 21, 3675, 'units_0.3675.nxt', '112f0e8c019d9c2873b5676ee4e1a691', 0), -(1312, 22, 3675, 'etc_0.3675.nxt', '2bf5a88f0cd370dcc037a96627ecc81e', 0), -(1313, 1, 3675, 'ForgedAlliance.3675.exe', '54ad49b7838abfc1d498783614a0176a', 0), -(1857, 22, 3676, 'etc.3676.nxt', 'd144f2cb8e33d2999e5e170c13610bf1', 0), -(1858, 12, 3676, 'env.3676.nxt', '12a3bdf9584b8c1bb0fe27a360402139', 0), -(1859, 11, 3676, 'effects.3676.nxt', 'd07e0b2f9eee408dd0f3755ab20a9b0a', 0), -(1860, 21, 3676, 'units.3676.nxt', 'f53622ec56e720f3e90c50499d0ff3c9', 0), -(1861, 15, 3676, 'meshes.3676.nxt', '79ee53ca01b0bc8c53681b7b07340c50', 0), -(1862, 18, 3676, 'projectiles.3676.nxt', 'f6df327ad9cfa9fa0b7f9864d737701d', 0), -(1863, 19, 3676, 'schook.3676.nxt', '5f612cff52676380d3c0eab16561248c', 0), -(1864, 14, 3676, 'lua.3676.nxt', 'bbe52e8b6b742d0f2b00c88b1df70203', 0), -(1865, 13, 3676, 'loc.3676.nxt', '1a004a252e69fb7f5bf01f953693c81b', 0), -(1866, 20, 3676, 'textures.3676.nxt', 'c6700711c7fdcfc8d1ed96ae5df977b1', 0), -(1867, 1, 3676, 'ForgedAlliance.3676.exe', 'cb9d3ef03be5800ce761c9e4914d9b86', 0), -(1979, 22, 3677, 'etc.3677.nxt', '2e4d342edef0d06d607bd2f1c0bba91d', 0), -(1980, 12, 3677, 'env.3677.nxt', '5ec78d878741926662a0f64169884acb', 0), -(1981, 11, 3677, 'effects.3677.nxt', 'f0628838c57c7284b87354a5a6e32f27', 0), -(1982, 21, 3677, 'units.3677.nxt', 'd07495b009151c00216aa468ba775a25', 0), -(1983, 15, 3677, 'meshes.3677.nxt', '06e52e245b29653237782b3768165f00', 0), -(1984, 18, 3677, 'projectiles.3677.nxt', '61473752278829fc95647f973c706d04', 0), -(1985, 19, 3677, 'schook.3677.nxt', '767b2025a66cecfa11f89760e6a260ac', 0), -(1986, 14, 3677, 'lua.3677.nxt', '5d5cc02199e6cfba6531bc38aa87347d', 0), -(1987, 13, 3677, 'loc.3677.nxt', 'da07496ec292c67ea6220160adffb392', 0), -(1988, 20, 3677, 'textures.3677.nxt', '1a6e17e2f0aa2dc0a4a8e415200a2fbb', 0), -(1989, 1, 3677, 'ForgedAlliance.3677.exe', '2b703fefa308f13cea32482bf8b464d6', 0), -(2001, 22, 3680, 'etc.3680.nxt', 'b1f2da0273dbfb2d9dec6aecace4f752', 0), -(2002, 12, 3680, 'env.3680.nxt', 'bcf999bd23d5c145107fa21e660ed659', 0), -(2003, 11, 3680, 'effects.3680.nxt', '0e588c32d628a5ea9f860f19b92a9b70', 0), -(2004, 21, 3680, 'units.3680.nxt', '052e15428aac4015dbfc3fc645dfad22', 0), -(2005, 15, 3680, 'meshes.3680.nxt', '276451be231bae8c296803e716f7ef84', 0), -(2006, 18, 3680, 'projectiles.3680.nxt', 'bf0ff098bc713431f33e2507388e911d', 0), -(2007, 19, 3680, 'schook.3680.nxt', '10e31edb645ac05196b43dbad8625757', 0), -(2008, 14, 3680, 'lua.3680.nxt', '20c4b451d40a52dd97b192f9c52d2923', 0), -(2009, 13, 3680, 'loc.3680.nxt', 'feeaaec787616864b5162d36d74d9185', 0), -(2010, 20, 3680, 'textures.3680.nxt', 'c265f40ddb36d3146ba3fbd79951fe52', 0), -(2011, 1, 3680, 'ForgedAlliance.3680.exe', '26fc5a5a8e56c86684c989d3795fc8b8', 0), -(2122, 22, 3681, 'etc.3681.nxt', 'e68bb21d55b69448eaa7c49757932b3e', 0), -(2123, 12, 3681, 'env.3681.nxt', '19396af2616c366ac44c5400c04566a6', 0), -(2124, 11, 3681, 'effects.3681.nxt', '91269553a81c786ceae9b042de48daab', 0), -(2125, 21, 3681, 'units.3681.nxt', 'e6cd004d96a9ce6798d21ac6c8d5dc79', 0), -(2126, 15, 3681, 'meshes.3681.nxt', '62e0343eb7c329175b393133f901fbad', 0), -(2127, 18, 3681, 'projectiles.3681.nxt', '11d9a6cdf5c699b9964d1e0fbc89226b', 0), -(2128, 19, 3681, 'schook.3681.nxt', 'f3f0e7f5b3eb8cbdc46c9423a3b5e8e7', 0), -(2129, 14, 3681, 'lua.3681.nxt', 'e3fc51a70b7a4e71f759ca16d6bc13a8', 0), -(2130, 13, 3681, 'loc.3681.nxt', '76e95d68bdc62593fedafaf8385c7243', 0), -(2131, 20, 3681, 'textures.3681.nxt', '4a4f6d5075c6997b56fedc0edd4df824', 0), -(2132, 1, 3681, 'ForgedAlliance.3681.exe', '0e9d74f715ac757e3b35c6d14d5904a9', 0), -(2221, 22, 3682, 'etc.3682.nx5', 'e16647f042a4049a00973d34e8e4f898', 0), -(2222, 12, 3682, 'env.3682.nx5', '01a48db1a5fe44a14755e19ed1dc8a2d', 0), -(2223, 11, 3682, 'effects.3682.nx5', 'ca693fb1beeef2e328c3c3b04b38f0d3', 0), -(2224, 21, 3682, 'units.3682.nx5', '645a6dfe52aa1adefd66e46be893ac45', 0), -(2225, 15, 3682, 'meshes.3682.nx5', 'ac1c7593891d2da31298394726c7dfeb', 0), -(2226, 18, 3682, 'projectiles.3682.nx5', '048ee840fd2f5d277d908934aefe009f', 0), -(2227, 19, 3682, 'schook.3682.nx5', 'bd219697990f8538aeff783574319ded', 0), -(2228, 14, 3682, 'lua.3682.nx5', '2fdda427cfd201cc522c02086a9f4f7c', 0), -(2229, 13, 3682, 'loc.3682.nx5', '4e18a100517e1433d9cf5f9817f36603', 0), -(2230, 20, 3682, 'textures.3682.nx5', '8c60e0b214952e8f78693ed24945596b', 0), -(2231, 1, 3682, 'ForgedAlliance.3682.exe', '334e86d0e0531e3539b1e3c8111e452c', 0), -(2562, 22, 3684, 'etc.3684.nx5', '66ce0ef65130ab5cb81e78abc3936c03', 0), -(2563, 12, 3684, 'env.3684.nx5', '4a62907c19ba991b13eac31526dc209f', 0), -(2564, 11, 3684, 'effects.3684.nx5', 'f99f83007f1ce6f6195b4c4963c1ff34', 0), -(2565, 21, 3684, 'units.3684.nx5', '0bfe7094e292bd591fac8d68708b959f', 0), -(2566, 15, 3684, 'meshes.3684.nx5', '249645a4524dd07a27ec6411bbe1403a', 0), -(2567, 18, 3684, 'projectiles.3684.nx5', 'edf422877f0814c15442ad579ae3b367', 0), -(2568, 19, 3684, 'schook.3684.nx5', 'ea0889a39384543c80ac3a249065c202', 0), -(2569, 14, 3684, 'lua.3684.nx5', 'c3edd3c545ada55f7c4b475dcfe26983', 0), -(2570, 13, 3684, 'loc.3684.nx5', 'ab659d90d242d18ffbbb9e072d374d84', 0), -(2571, 20, 3684, 'textures.3684.nx5', '6101d812e5e3a754f0520be130dafd5d', 0), -(2572, 1, 3684, 'ForgedAlliance.3684.exe', '0f9e085d44da00e0ce314c181cf087a8', 0), -(2672, 22, 3686, 'etc.3686.nx5', 'f19941aeece3e058ad241751574bc597', 0), -(2673, 12, 3686, 'env.3686.nx5', '631738db2a1b1294c86f6c8d17a47510', 0), -(2674, 11, 3686, 'effects.3686.nx5', 'b558676a9041948c7abd466a25d1de27', 0), -(2675, 21, 3686, 'units.3686.nx5', '42171f5c9e6db71dbb062845c7a1c60d', 0), -(2676, 1, 3686, 'ForgedAlliance.3686.exe', '4d939f813fcf987435669719e703ca89', 0), -(2721, 12, 3689, 'env.3689.nx5', '43b16f458187031120415d7d181d343e', 0), -(2722, 15, 3689, 'meshes.3689.nx5', 'a45675e23460ddc1fc7a8a3c074f8bb6', 0), -(2723, 19, 3689, 'schook.3689.nx5', '242e4b2e659e1efcaaba73beecf12d63', 0), -(2724, 14, 3689, 'lua.3689.nx5', '24b8cc6fe20b777e8d7ad5723734a145', 0), -(2725, 22, 3689, 'etc.3689.nx5', '5515f32cf94f4e7139bfbac4ed0bb136', 0), -(2726, 11, 3689, 'effects.3689.nx5', '5559e01e9b8d24fb94a0033f4ec8c819', 0), -(2727, 20, 3689, 'textures.3689.nx5', 'b8446b90c4bde7f92916c3a4fc6f55f4', 0), -(2728, 18, 3689, 'projectiles.3689.nx5', '69086d8ac43c01e44fb6bad7d6fadc4a', 0), -(2729, 13, 3689, 'loc.3689.nx5', '127b1f96afc715db6fd5e4fdcaa609ed', 0), -(2730, 21, 3689, 'units.3689.nx5', '18ed96f4e1e5e27855045015a3ce9136', 0), -(2731, 1, 3689, 'ForgedAlliance.3689.exe', '6ca19563c80509050f7693cd19d1526e', 0), -(2754, 12, 3692, 'env.3692.nx5', '28dd67ded9e108daff490b66b5640584', 0), -(2755, 15, 3692, 'meshes.3692.nx5', 'b3acbd07e654731b612656fa222f006b', 0), -(2756, 19, 3692, 'schook.3692.nx5', '8f71b1d1ab63b896313b634f3d677430', 0), -(2757, 14, 3692, 'lua.3692.nx5', '0342fe9bcbabd101f905c8280dbe0bf0', 0), -(2758, 22, 3692, 'etc.3692.nx5', '54629e2541127ec20621e241962bc2ad', 0), -(2759, 11, 3692, 'effects.3692.nx5', 'e59967fbaf767364d551e5e30fb5dda7', 0), -(2760, 20, 3692, 'textures.3692.nx5', '89aed3034894a0e1834de753633a75f7', 0), -(2761, 18, 3692, 'projectiles.3692.nx5', '0f94413ffecf3a9ce63aac56b0b4bcc8', 0), -(2762, 13, 3692, 'loc.3692.nx5', '6abfacf9e53c9b198d47574901568e4c', 0), -(2763, 21, 3692, 'units.3692.nx5', '37bc714b0fa72502fcb9b3c366fffb54', 0), -(2764, 1, 3692, 'ForgedAlliance.3692.exe', '9aa457e1e32dbbf3f188ecb08db4db3d', 0), -(2776, 12, 3693, 'env.3693.nx5', '5012ee4765b99c71c08a8e4c851d57d8', 0), -(2777, 15, 3693, 'meshes.3693.nx5', 'f5a6e6b897b9d66a8127ed8ad3019255', 0), -(2778, 19, 3693, 'schook.3693.nx5', 'b2a99e560214b6601a6d4931a519c155', 0), -(2779, 14, 3693, 'lua.3693.nx5', '92f2ad5ee57f0c5f18f16b06c45d02b6', 0), -(2780, 22, 3693, 'etc.3693.nx5', '0debd69000a4f48c7b1ed5cc6a950f7d', 0), -(2781, 11, 3693, 'effects.3693.nx5', 'f424e20adcf364c0a6cb54f8d253db9a', 0), -(2782, 20, 3693, 'textures.3693.nx5', '46ebc4eb5d0f8be805c743aff7f4e3fc', 0), -(2783, 18, 3693, 'projectiles.3693.nx5', '4b3264e5495a4749aeb1c7869a079b25', 0), -(2784, 13, 3693, 'loc.3693.nx5', '8fc9e43021614f61bd1e17eec0fb6f9c', 0), -(2785, 21, 3693, 'units.3693.nx5', '70c92a904913256d8557a26cc74a76b3', 0), -(2786, 1, 3693, 'ForgedAlliance.3693.exe', '653d9b3d68bedc34c25969a6c021ed26', 0), -(2809, 12, 3695, 'env.3695.nx5', '97b307ed18545a77c4aa5cec3e50c5fa', 0), -(2810, 15, 3695, 'meshes.3695.nx5', '847f642e0e60c687ba5465741d3ebcb4', 0), -(2811, 19, 3695, 'schook.3695.nx5', '57c1d22b81bbf4c59a85d695b7a61558', 0), -(2812, 14, 3695, 'lua.3695.nx5', 'bb03e905e5b647ad8c3636c427da2156', 0), -(2813, 22, 3695, 'etc.3695.nx5', '5fb5dc45ed91ea10f8e6374ec33c9a89', 0), -(2814, 11, 3695, 'effects.3695.nx5', 'cb2eacedd14d5e0e5df6999e18f99670', 0), -(2815, 20, 3695, 'textures.3695.nx5', '7e1b19599486602f49274abb0ed9d4a9', 0), -(2816, 18, 3695, 'projectiles.3695.nx5', '8fb19a7b1fbab53a460b8bd150ea2b89', 0), -(2817, 13, 3695, 'loc.3695.nx5', 'b82f4cc0e177e8d894fbffe579b9aedd', 0), -(2818, 21, 3695, 'units.3695.nx5', 'e297f043b9f3e802d8cbf956548d29f2', 0), -(2819, 1, 3695, 'ForgedAlliance.3695.exe', '199c53981051617a87ed15a787961572', 0), -(2930, 12, 3696, 'env.3696.nx5', '52edd5da539b9f57245775c107c22b51', 0), -(2931, 15, 3696, 'meshes.3696.nx5', '801676f280793f9d9146b3de516756dc', 0), -(2932, 19, 3696, 'schook.3696.nx5', '0a69570c165f64129a5e63f3a95606a6', 0), -(2933, 14, 3696, 'lua.3696.nx5', '287d294d54e0e9181ac0e42c7d23bc8c', 0), -(2934, 22, 3696, 'etc.3696.nx5', '935db5f25054cded455788797649fbfa', 0), -(2935, 11, 3696, 'effects.3696.nx5', '30c75a62927676217e3b49780646f8d5', 0), -(2936, 20, 3696, 'textures.3696.nx5', 'c169378b8a798c906b18cdfe9ea9998e', 0), -(2937, 18, 3696, 'projectiles.3696.nx5', 'e95b9ab0c571bdbe90b252b604ef46cb', 0), -(2938, 13, 3696, 'loc.3696.nx5', 'eafc55938746388c1cebf0e0b2d79ec8', 0), -(2939, 21, 3696, 'units.3696.nx5', 'de611fc390578c64b0889b56e278f681', 0), -(2940, 1, 3696, 'ForgedAlliance.3696.exe', '3050ad5780eb9ad1711859a9f25a67c3', 0), -(2975, 12, 3697, 'env.3697.nx5', 'eb2e564492dc30e4648d79545419f54f', 0), -(2976, 15, 3697, 'meshes.3697.nx5', '08c2bfdc0e5b7eeecec8b547b570bd99', 0), -(2977, 19, 3697, 'schook.3697.nx5', '78a9dd27f06d62608c1ef9c46b43c9e9', 0), -(2978, 14, 3697, 'lua.3697.nx5', 'c2ac724c53eb14f2962a24fe53651cb9', 0), -(2979, 22, 3697, 'etc.3697.nx5', 'b9a1233df750fd32ad31838dc417a04e', 0), -(2980, 11, 3697, 'effects.3697.nx5', 'b38fcb37a0f3c41aa51a9f873a5049c6', 0), -(2981, 20, 3697, 'textures.3697.nx5', '0ff641bccdce28aaa78f51a286b5f495', 0), -(2982, 18, 3697, 'projectiles.3697.nx5', '3eb421ec6b8ad51926bfe4e00e0ab21c', 0), -(2983, 13, 3697, 'loc.3697.nx5', '810318da25c4d283ab40ee0a2124b226', 0), -(2984, 21, 3697, 'units.3697.nx5', '52f46a380e136275cede302bcb407128', 0), -(2985, 1, 3697, 'ForgedAlliance.3697.exe', '83bd429f0f2f119a4539faa48f67c71c', 0), -(3250, 12, 3698, 'env.3698.nx5', 'e9a409a30bf100afb701c18719afe789', 0), -(3251, 15, 3698, 'meshes.3698.nx5', 'a20a9d513e56e8e2620373b047365469', 0), -(3252, 19, 3698, 'schook.3698.nx5', '5e3aabf39c41ae625261c01fed9b445e', 0), -(3253, 14, 3698, 'lua.3698.nx5', 'd0911dcecb0d60eb72deff9aed137eaa', 0), -(3254, 22, 3698, 'etc.3698.nx5', '90490c5f4c207dbb7f6d8f6912940a4d', 0), -(3255, 11, 3698, 'effects.3698.nx5', 'b5a7062c1ff354a99b4f36093f6dadc4', 0), -(3256, 20, 3698, 'textures.3698.nx5', 'a8ef3bbef23a04d9a8496c0f892bac9a', 0), -(3257, 18, 3698, 'projectiles.3698.nx5', 'dcf9f558444a6706a4e50f4362706659', 0), -(3258, 13, 3698, 'loc.3698.nx5', '70ff10e21bdb0d0f9b524b3c9bab1dd6', 0), -(3259, 21, 3698, 'units.3698.nx5', '7d0aa197f5df098b899a6eebb7945fae', 0), -(3260, 1, 3698, 'ForgedAlliance.3698.exe', 'c0f936b1c9675f57b688ad00a5c486de', 0), -(3272, 12, 3699, 'env.3699.nx5', 'e732ce847e35e5cd9e420b4d9ff63fc1', 0), -(3273, 15, 3699, 'meshes.3699.nx5', 'c3a91ea2e7690d5436e5f09cbc09512a', 0), -(3274, 19, 3699, 'schook.3699.nx5', '5c7295576d95900e40e85519fdea54e7', 0), -(3275, 14, 3699, 'lua.3699.nx5', '9013cb049fa677994448670cd17081d7', 0), -(3276, 22, 3699, 'etc.3699.nx5', 'e653ef1ce9182ce1b48b91b1db2f1f6c', 0), -(3277, 11, 3699, 'effects.3699.nx5', 'f88f432e31e55cf2c6da4ac210e29a7a', 0), -(3278, 20, 3699, 'textures.3699.nx5', 'cc1baf98bf2565c02f6bcae268ad4cf5', 0), -(3279, 18, 3699, 'projectiles.3699.nx5', '17f490bc88a2f8e393491c1d66df5b9e', 0), -(3280, 13, 3699, 'loc.3699.nx5', '412b39d81a8966c0ad32514b0bd6419f', 0), -(3281, 21, 3699, 'units.3699.nx5', '9e028cfb8262a594109eb2cb87450abc', 0), -(3282, 1, 3699, 'ForgedAlliance.3699.exe', 'a269c439b663bae1a3fb90bef6ad03a8', 0), -(3360, 12, 3700, 'env.3700.nx5', '5c51a96c0c9e724bfa353a1afa19d70a', 0), -(3361, 15, 3700, 'meshes.3700.nx5', '261fa982d651fa1aeae84723734d0c01', 0), -(3362, 19, 3700, 'schook.3700.nx5', '92d50557cf75054e4d1fb99278bb03a8', 0), -(3363, 14, 3700, 'lua.3700.nx5', '318d5939ddd4995ea2ecf4edab1ab5d3', 0), -(3364, 22, 3700, 'etc.3700.nx5', 'b407bbffb36e846e67bf79941381a3a1', 0), -(3365, 11, 3700, 'effects.3700.nx5', 'fc545dc1e75e288a1a1958c07992f3b6', 0), -(3366, 20, 3700, 'textures.3700.nx5', 'ad8c6508b2a20b188a1c303a53605702', 0), -(3367, 18, 3700, 'projectiles.3700.nx5', 'd7939dc69f4de576000a88ee32912c86', 0), -(3368, 13, 3700, 'loc.3700.nx5', '2552498024fb54697b0fc0042f9e6ccd', 0), -(3369, 21, 3700, 'units.3700.nx5', '88444f32bb11bf9b5f8dc744a6fdca22', 0), -(3370, 1, 3700, 'ForgedAlliance.3700.exe', 'e47d09b9b2ac5c1e1562b3393d47ceb6', 0), -(3569, 12, 3701, 'env.3701.nx5', '3a33074a804e5a7a631f8a6406ac8e54', 0), -(3570, 15, 3701, 'meshes.3701.nx5', '4c7c2e285b1827704f541c54965a5ee4', 0), -(3571, 19, 3701, 'schook.3701.nx5', '2054c4c7ec6129a7ffd0ba1c1908c887', 0), -(3572, 14, 3701, 'lua.3701.nx5', '88d58e295c4fec77a9e41ac8db9c8814', 0), -(3573, 22, 3701, 'etc.3701.nx5', 'de1ca89f8e73b518f8a438ecc6edeccd', 0), -(3574, 11, 3701, 'effects.3701.nx5', 'aab6977a716613b98eca849e50bbb081', 0), -(3575, 20, 3701, 'textures.3701.nx5', '7eaef838c78522d688f0f422b6b6a77c', 0), -(3576, 18, 3701, 'projectiles.3701.nx5', '4be08f51cf00bd56a9b9e74223f12c60', 0), -(3577, 13, 3701, 'loc.3701.nx5', 'c24e10e8af849ae5dfa4a85a85fbe46f', 0), -(3578, 21, 3701, 'units.3701.nx5', 'efb20720ef77ca7de09578e206de2623', 0), -(3579, 1, 3701, 'ForgedAlliance.3701.exe', 'f11fdb5c09a8d8999f90358560f5e627', 0), -(3690, 12, 3702, 'env.3702.nx5', '336dba49f9ab129bb9db7319038e9927', 0), -(3691, 15, 3702, 'meshes.3702.nx5', 'a62acb96c6fbdd313306f7860b2d3726', 0), -(3692, 19, 3702, 'schook.3702.nx5', '23060a3f11abae6dcab83a23eba187d3', 0), -(3693, 14, 3702, 'lua.3702.nx5', '03cb2004eb0e9421e3063ce9dcfdd7af', 0), -(3694, 22, 3702, 'etc.3702.nx5', 'bd6ef8fceb2e0b2c880fdb8bc3143cb8', 0), -(3695, 11, 3702, 'effects.3702.nx5', '3da4a4887633b6efbf6e46210bfd6fd4', 0), -(3696, 20, 3702, 'textures.3702.nx5', '138c40f5face903fb52d2977e444a122', 0), -(3697, 18, 3702, 'projectiles.3702.nx5', '55a943779ca3a73d8dc203e0fef87e9e', 0), -(3698, 13, 3702, 'loc.3702.nx5', '95a42c088d041f45a0a675a807c5b3be', 0), -(3699, 21, 3702, 'units.3702.nx5', '917bf58604eb0284c2f99521013b39cc', 0), -(3700, 1, 3702, 'ForgedAlliance.3702.exe', '819558d8cf924dce94c038882e6ed229', 0), -(3855, 12, 3703, 'env.3703.nx5', 'd32462eb04d059fdfd76ff969527f05c', 0), -(3856, 15, 3703, 'meshes.3703.nx5', 'cdcbbd1f612d2f1f20f3c1d09a04d288', 0), -(3857, 19, 3703, 'schook.3703.nx5', '518b50174270e15c2bbf721d0af20e3b', 0), -(3858, 14, 3703, 'lua.3703.nx5', '18ea0e3ec10e6651b5e64edfa34c4215', 0), -(3859, 22, 3703, 'etc.3703.nx5', '06d9f282bfda5189c68e26f50a615c1b', 0), -(3860, 11, 3703, 'effects.3703.nx5', '816e4222eef38f922d0efed815aa3161', 0), -(3861, 20, 3703, 'textures.3703.nx5', 'b2c29fc0dedf42d406fef99f1d42dcb5', 0), -(3862, 18, 3703, 'projectiles.3703.nx5', '475db0befe255d2362499b79ed2c7db5', 0), -(3863, 13, 3703, 'loc.3703.nx5', 'c3df5a3e916d2d19c8fd1d7e9aadc599', 0), -(3864, 21, 3703, 'units.3703.nx5', '2e5d0ecdf922240b190977ff7b956db2', 0), -(3865, 1, 3703, 'ForgedAlliance.3703.exe', '80d1355256229ab53ba6f24db1fd6a7f', 0), -(3965, 12, 3704, 'env.3704.nx5', '3e28c9290eef7a4e95d0d7aceeead656', 0), -(3966, 15, 3704, 'meshes.3704.nx5', 'de7f1c8ffed4979e4709e86850d469f0', 0), -(3967, 19, 3704, 'schook.3704.nx5', '799b598b81e4b33822e938d7c34d9110', 0), -(3968, 14, 3704, 'lua.3704.nx5', '4ae8ce5e9942a273f902543686df6a67', 0), -(3969, 22, 3704, 'etc.3704.nx5', '5123617450af795abd529d20f8be0f4d', 0), -(3970, 11, 3704, 'effects.3704.nx5', '612fb4cdc033ade7a8046fcaa4a26df6', 0), -(3971, 20, 3704, 'textures.3704.nx5', 'fa337107791ec1fb32f83519c83fa92b', 0), -(3972, 18, 3704, 'projectiles.3704.nx5', '8b79a1563a9ac7c55e656577b7569820', 0), -(3973, 13, 3704, 'loc.3704.nx5', '210450e633b373484ae17cdef4ec6d54', 0), -(3974, 21, 3704, 'units.3704.nx5', '61a7f6d8ed402f3be71c183b034fb31d', 0), -(3975, 1, 3704, 'ForgedAlliance.3704.exe', 'c3d48349ce61d9243046a1b1a4428912', 0), -(4097, 12, 3705, 'env.3705.nx5', '5ff9c2b8c3cc550ab2c0e5aea2422b11', 0), -(4098, 15, 3705, 'meshes.3705.nx5', 'c557257a69bf0aa932ddc01ea5457760', 0), -(4099, 19, 3705, 'schook.3705.nx5', '4cb441557a89d772245991bb97da48a1', 0), -(4100, 14, 3705, 'lua.3705.nx5', '80c12c6cbe15fd2f7119ad4e0dc1ceb5', 0), -(4101, 22, 3705, 'etc.3705.nx5', 'faeaa5094f600cec27cb28dfbb4ee210', 0), -(4102, 11, 3705, 'effects.3705.nx5', '7c46f280170c87a3b408436f6c4c3c1a', 0), -(4103, 20, 3705, 'textures.3705.nx5', '35a698d575b9a1c5036dee1f195ff6da', 0), -(4104, 18, 3705, 'projectiles.3705.nx5', '6d851bc2913031fdea04d912b8871b62', 0), -(4105, 13, 3705, 'loc.3705.nx5', '84d46f35a637e600687cee721d99c442', 0), -(4106, 21, 3705, 'units.3705.nx5', '8d8202323d66d062193aa7f66f718316', 0), -(4107, 1, 3705, 'ForgedAlliance.3705.exe', '573296042dc5eb6e13c4ba5f7b855539', 0), -(4119, 12, 3706, 'env.3706.nx5', 'b9a854b4c3d6d24fd6d537661746a3fa', 0), -(4120, 15, 3706, 'meshes.3706.nx5', '882b79404ab791b08713071c84316179', 0), -(4121, 19, 3706, 'schook.3706.nx5', 'fe10bf92f58db6656c3b403d2c3405c2', 0), -(4122, 14, 3706, 'lua.3706.nx5', 'f114a76320765837316dfad0edbdf8bd', 0), -(4123, 22, 3706, 'etc.3706.nx5', '201e60463bff73402d8b1016d2758cf9', 0), -(4124, 11, 3706, 'effects.3706.nx5', '2641a986264112dd6d4293e06a6146b5', 0), -(4125, 20, 3706, 'textures.3706.nx5', '783af5877d8b8a77cba25526f0f6466f', 0), -(4126, 18, 3706, 'projectiles.3706.nx5', '14a804000945dc430198970efef25fbb', 0), -(4127, 13, 3706, 'loc.3706.nx5', '96d67c3f7af0a9888e3d2dc227a4d170', 0), -(4128, 21, 3706, 'units.3706.nx5', '4759b0c48c072df42d21b8162c79af73', 0), -(4129, 1, 3706, 'ForgedAlliance.3706.exe', 'c20b922a785cf5876c39b7696a16f162', 0), -(4317, 12, 3707, 'env.3707.nx5', 'a261b4c97679aa69fdccc9ed2c1aaa97', 0), -(4318, 15, 3707, 'meshes.3707.nx5', 'eed1e3a85dc20296b5d29f2a51471728', 0), -(4319, 19, 3707, 'schook.3707.nx5', 'b8245efcd38211a8b04db584ab8ffa27', 0), -(4320, 14, 3707, 'lua.3707.nx5', 'e648b55afbc94cbc0cbf603354f7b28e', 0), -(4321, 22, 3707, 'etc.3707.nx5', '3bd928096ed69156143daa0c38fb8955', 0), -(4322, 11, 3707, 'effects.3707.nx5', 'f9e92b4535117907cc187ad255e6397f', 0), -(4323, 20, 3707, 'textures.3707.nx5', '748e6f92720ea5dac7a52b4ac05449d1', 0), -(4324, 18, 3707, 'projectiles.3707.nx5', 'a57e08debfaba57532f2695580c6f45f', 0), -(4325, 13, 3707, 'loc.3707.nx5', '9fbf48ac4f13432ddbf056b7a38a7edb', 0), -(4326, 21, 3707, 'units.3707.nx5', '30d14d3365f28777c53ae5b288c1fc2d', 0), (4327, 1, 3707, 'ForgedAlliance.3707.exe', '79f0ea70625ab464d369721183e9fd29', 0); diff --git a/src/main/java/com/faforever/api/deployment/ExeUploaderController.java b/src/main/java/com/faforever/api/deployment/ExeUploaderController.java index bde2eef89..538316aac 100644 --- a/src/main/java/com/faforever/api/deployment/ExeUploaderController.java +++ b/src/main/java/com/faforever/api/deployment/ExeUploaderController.java @@ -54,6 +54,6 @@ public void upload(@RequestParam("file") MultipartFile file, log.info("Uploading exe file '{}' to '{}' directory", file.getOriginalFilename(), modName); - exeUploaderService.processUpload(file.getBytes(), modName); + exeUploaderService.processUpload(file.getInputStream(), modName); } } diff --git a/src/main/java/com/faforever/api/deployment/ExeUploaderService.java b/src/main/java/com/faforever/api/deployment/ExeUploaderService.java index f15170598..8fcf1cf47 100644 --- a/src/main/java/com/faforever/api/deployment/ExeUploaderService.java +++ b/src/main/java/com/faforever/api/deployment/ExeUploaderService.java @@ -14,6 +14,8 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.util.Assert; +import java.io.File; +import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -23,6 +25,7 @@ import static com.google.common.hash.Hashing.md5; import static com.google.common.io.Files.hash; +import static java.nio.file.Files.copy; import static java.nio.file.Files.createDirectories; @Service @@ -44,28 +47,28 @@ public ExeUploaderService( @Transactional @SneakyThrows - public void processUpload(byte[] bytes, String modName) { + public void processUpload(InputStream exeDataInputStream, String modName) { checkAllowedBranchName(modName); FeaturedModFile featuredModFile = featuredModService.getFile(modName, null, "ForgedAlliance.exe"); featuredModFile.setName(String.format("ForgedAlliance.%d.exe", featuredModFile.getVersion())); Path uploadedFile = this.upload( - bytes, + exeDataInputStream, featuredModFile.getName(), modName ); featuredModFile.setMd5(hash(uploadedFile.toFile(), md5()).toString()); - + exeDataInputStream.close(); List featuredModFiles = Collections.singletonList(featuredModFile); featuredModService.save(modName, (short) featuredModFile.getVersion(), featuredModFiles); } @SneakyThrows - public Path upload(byte[] exeData, String fileName, String modName) { - Assert.isTrue(exeData.length > 0, "data of 'ForgedAlliance.exe' must not be empty"); + public Path upload(InputStream exeDataInputStream, String fileName, String modName) { + Assert.isTrue(exeDataInputStream.available() > 0, "data of 'ForgedAlliance.exe' must not be empty"); Path tempDir = contentService.createTempDir(); Path temporaryFile = tempDir.resolve(fileName); - Files.write(temporaryFile, exeData); + Files.copy(exeDataInputStream, temporaryFile); Path copyTo = getCopyToPath(modName, fileName); createDirectories(copyTo.getParent(), FilePermissionUtil.directoryPermissionFileAttributes()); @@ -83,10 +86,18 @@ private void checkAllowedBranchName(String modName) throws ApiException { } private Path getCopyToPath(String modName, String fileName) { - String copyTo = apiProperties.getDeployment().getForgedAllianceBetaExePath(); - if ("fafdevelop".equals(modName)) { - copyTo = apiProperties.getDeployment().getForgedAllianceDevelopExePath(); + String copyTo = null; + switch (modName) { + case "fafbeta": + copyTo = apiProperties.getDeployment().getForgedAllianceBetaExePath(); + break; + case "fafdevelop": + copyTo = apiProperties.getDeployment().getForgedAllianceDevelopExePath(); + break; + default: + throw new ApiException(new Error(ErrorCode.INVALID_FEATURED_MOD, modName)); } + return Paths.get(copyTo, fileName); } } diff --git a/src/main/java/com/faforever/api/featuredmods/LegacyFeaturedModFileRepository.java b/src/main/java/com/faforever/api/featuredmods/LegacyFeaturedModFileRepository.java index c5d2d77dd..83c6bbaf6 100644 --- a/src/main/java/com/faforever/api/featuredmods/LegacyFeaturedModFileRepository.java +++ b/src/main/java/com/faforever/api/featuredmods/LegacyFeaturedModFileRepository.java @@ -21,6 +21,7 @@ public LegacyFeaturedModFileRepository(EntityManager entityManager) { public FeaturedModFile getFile(String modName, Integer version, String fileName) { List files = getFiles(modName, version, fileName); + Assert.state(files.size() > 0); return files.get(0); } diff --git a/src/test/java/com/faforever/api/deployment/ExeUploaderServiceTest.java b/src/test/java/com/faforever/api/deployment/ExeUploaderServiceTest.java index 1c5a14709..563c4e351 100644 --- a/src/test/java/com/faforever/api/deployment/ExeUploaderServiceTest.java +++ b/src/test/java/com/faforever/api/deployment/ExeUploaderServiceTest.java @@ -15,6 +15,8 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; +import java.io.ByteArrayInputStream; +import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Paths; import java.util.List; @@ -42,7 +44,7 @@ public class ExeUploaderServiceTest { private FafApiProperties.Deployment deployment; @Mock private FeaturedModService featuredModService; - private byte[] bytes; + private InputStream exeDataInputStream; private FeaturedModFile featuredModFile; @@ -52,7 +54,7 @@ public void setUp() { instance = new ExeUploaderService(contentService, apiProperties, featuredModService); when(apiProperties.getDeployment()).thenReturn(deployment); - bytes = new byte[]{1, 2, 3, 4}; + exeDataInputStream = new ByteArrayInputStream(new byte[] {1, 2, 3, 4}); when(contentService.createTempDir()).thenReturn(temporaryDirectory.getRoot().toPath()); featuredModFile = new FeaturedModFile(); @@ -75,7 +77,7 @@ public void testProcessUploadBeta() { when(featuredModService.getFile(modName, null, "ForgedAlliance.exe")).thenReturn( featuredModFile); - instance.processUpload(bytes, modName); + instance.processUpload(exeDataInputStream, modName); assertTrue(Files.exists(Paths.get(finalExeDestination))); ArgumentCaptor> modFilesCaptor = ArgumentCaptor.forClass(List.class); @@ -98,7 +100,7 @@ public void testProcessUploadDevelop() { when(featuredModService.getFile(modName, null, "ForgedAlliance.exe")).thenReturn( featuredModFile); - instance.processUpload(bytes, modName); + instance.processUpload(exeDataInputStream, modName); assertTrue(Files.exists(Paths.get(finalExeDestination))); ArgumentCaptor> modFilesCaptor = ArgumentCaptor.forClass(List.class); @@ -108,7 +110,6 @@ public void testProcessUploadDevelop() { verify(contentService).createTempDir(); verify(apiProperties, atLeastOnce()).getDeployment(); - verify(deployment).getForgedAllianceBetaExePath(); verify(deployment).getForgedAllianceDevelopExePath(); verify(featuredModService).getFile(modName, null, "ForgedAlliance.exe"); } @@ -116,6 +117,6 @@ public void testProcessUploadDevelop() { @Test(expected = ApiException.class) public void testProcessUploadIsForbidden() { String modName = "faf"; - instance.processUpload(bytes, modName); + instance.processUpload(exeDataInputStream, modName); } } From c56b33835f5a85d9b9ae858f6dbd0ebbee960c61 Mon Sep 17 00:00:00 2001 From: Konstantin Kalinovsky Date: Sun, 8 Sep 2019 22:46:49 +0200 Subject: [PATCH 07/10] Ignoring tests, waiting for faf-db-migrations version 76, where we have tables that are needed for integration tests --- .../faforever/api/deployment/ExeUploadControllerTest.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/inttest/java/com/faforever/api/deployment/ExeUploadControllerTest.java b/src/inttest/java/com/faforever/api/deployment/ExeUploadControllerTest.java index 1424a12b7..e1a4a3bdc 100644 --- a/src/inttest/java/com/faforever/api/deployment/ExeUploadControllerTest.java +++ b/src/inttest/java/com/faforever/api/deployment/ExeUploadControllerTest.java @@ -2,6 +2,7 @@ import com.faforever.api.AbstractIntegrationTest; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.springframework.mock.web.MockMultipartFile; import org.springframework.test.context.jdbc.Sql; @@ -26,6 +27,7 @@ public void setUp() { } @Test + @Ignore("Waiting for faf-stack with faf-db-migrations:v76") public void testSuccessUploadBeta() throws Exception { this.mockMvc.perform(fileUpload("/exe/upload") .file(file) @@ -36,6 +38,7 @@ public void testSuccessUploadBeta() throws Exception { } @Test + @Ignore("Waiting for faf-stack with faf-db-migrations:v76") public void testSuccessUploadDevelop() throws Exception { this.mockMvc.perform(fileUpload("/exe/upload") .file(file) @@ -46,6 +49,7 @@ public void testSuccessUploadDevelop() throws Exception { } @Test + @Ignore("Waiting for faf-stack with faf-db-migrations:v76") public void testBadRequestUploadNoModName() throws Exception { this.mockMvc.perform(fileUpload("/exe/upload") .file(file) @@ -54,6 +58,7 @@ public void testBadRequestUploadNoModName() throws Exception { } @Test + @Ignore("Waiting for faf-stack with faf-db-migrations:v76") public void testBadRequestUploadNoFile() throws Exception { this.mockMvc.perform(fileUpload("/exe/upload") .param("modName", "fafdevelop") @@ -62,6 +67,7 @@ public void testBadRequestUploadNoFile() throws Exception { } @Test + @Ignore("Waiting for faf-stack with faf-db-migrations:v76") public void testBadRequestUploadFileWithWrongExeExtension() throws Exception { MockMultipartFile file = new MockMultipartFile("file", "ForgedAlliance.zip", "application/octet-stream", new byte[]{ 1, 2 ,3, 4 }); this.mockMvc.perform(fileUpload("/exe/upload") @@ -72,6 +78,7 @@ public void testBadRequestUploadFileWithWrongExeExtension() throws Exception { } @Test + @Ignore("Waiting for faf-stack with faf-db-migrations:v76") public void testBadRequestUploadWithoutApiKey() throws Exception { this.mockMvc.perform(fileUpload("/exe/upload") .file(file) @@ -80,6 +87,7 @@ public void testBadRequestUploadWithoutApiKey() throws Exception { } @Test + @Ignore("Waiting for faf-stack with faf-db-migrations:v76") public void testBadRequestUploadWithWrongApiKey() throws Exception { this.mockMvc.perform(fileUpload("/exe/upload") .file(file) From e7cdcb400b08ec7159b184e7c4e4a0d718e354e6 Mon Sep 17 00:00:00 2001 From: Konstantin Kalinovsky Date: Thu, 19 Sep 2019 23:48:13 +0200 Subject: [PATCH 08/10] Junit5 assertion + reenabled ignored tests --- .../faforever/api/deployment/ExeUploadControllerTest.java | 7 ------- .../faforever/api/deployment/ExeUploaderServiceTest.java | 7 +++++-- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/inttest/java/com/faforever/api/deployment/ExeUploadControllerTest.java b/src/inttest/java/com/faforever/api/deployment/ExeUploadControllerTest.java index e1a4a3bdc..029b4b156 100644 --- a/src/inttest/java/com/faforever/api/deployment/ExeUploadControllerTest.java +++ b/src/inttest/java/com/faforever/api/deployment/ExeUploadControllerTest.java @@ -27,7 +27,6 @@ public void setUp() { } @Test - @Ignore("Waiting for faf-stack with faf-db-migrations:v76") public void testSuccessUploadBeta() throws Exception { this.mockMvc.perform(fileUpload("/exe/upload") .file(file) @@ -38,7 +37,6 @@ public void testSuccessUploadBeta() throws Exception { } @Test - @Ignore("Waiting for faf-stack with faf-db-migrations:v76") public void testSuccessUploadDevelop() throws Exception { this.mockMvc.perform(fileUpload("/exe/upload") .file(file) @@ -49,7 +47,6 @@ public void testSuccessUploadDevelop() throws Exception { } @Test - @Ignore("Waiting for faf-stack with faf-db-migrations:v76") public void testBadRequestUploadNoModName() throws Exception { this.mockMvc.perform(fileUpload("/exe/upload") .file(file) @@ -58,7 +55,6 @@ public void testBadRequestUploadNoModName() throws Exception { } @Test - @Ignore("Waiting for faf-stack with faf-db-migrations:v76") public void testBadRequestUploadNoFile() throws Exception { this.mockMvc.perform(fileUpload("/exe/upload") .param("modName", "fafdevelop") @@ -67,7 +63,6 @@ public void testBadRequestUploadNoFile() throws Exception { } @Test - @Ignore("Waiting for faf-stack with faf-db-migrations:v76") public void testBadRequestUploadFileWithWrongExeExtension() throws Exception { MockMultipartFile file = new MockMultipartFile("file", "ForgedAlliance.zip", "application/octet-stream", new byte[]{ 1, 2 ,3, 4 }); this.mockMvc.perform(fileUpload("/exe/upload") @@ -78,7 +73,6 @@ public void testBadRequestUploadFileWithWrongExeExtension() throws Exception { } @Test - @Ignore("Waiting for faf-stack with faf-db-migrations:v76") public void testBadRequestUploadWithoutApiKey() throws Exception { this.mockMvc.perform(fileUpload("/exe/upload") .file(file) @@ -87,7 +81,6 @@ public void testBadRequestUploadWithoutApiKey() throws Exception { } @Test - @Ignore("Waiting for faf-stack with faf-db-migrations:v76") public void testBadRequestUploadWithWrongApiKey() throws Exception { this.mockMvc.perform(fileUpload("/exe/upload") .file(file) diff --git a/src/test/java/com/faforever/api/deployment/ExeUploaderServiceTest.java b/src/test/java/com/faforever/api/deployment/ExeUploaderServiceTest.java index 563c4e351..34fe93c91 100644 --- a/src/test/java/com/faforever/api/deployment/ExeUploaderServiceTest.java +++ b/src/test/java/com/faforever/api/deployment/ExeUploaderServiceTest.java @@ -9,6 +9,7 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.junit.jupiter.api.Assertions; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -114,9 +115,11 @@ public void testProcessUploadDevelop() { verify(featuredModService).getFile(modName, null, "ForgedAlliance.exe"); } - @Test(expected = ApiException.class) + @Test public void testProcessUploadIsForbidden() { String modName = "faf"; - instance.processUpload(exeDataInputStream, modName); + Assertions.assertThrows(ApiException.class, () -> { + instance.processUpload(exeDataInputStream, modName); + }); } } From b7cfc88a4b8a0c48b92fc21de5d1e318b6f3d397 Mon Sep 17 00:00:00 2001 From: Konstantin Kalinovsky Date: Tue, 24 Sep 2019 17:52:58 +0200 Subject: [PATCH 09/10] Migration to junit5 --- .../deployment/ExeUploaderControllerTest.java | 12 +- .../deployment/ExeUploaderServiceTest.java | 150 ++++++++++-------- 2 files changed, 89 insertions(+), 73 deletions(-) diff --git a/src/test/java/com/faforever/api/deployment/ExeUploaderControllerTest.java b/src/test/java/com/faforever/api/deployment/ExeUploaderControllerTest.java index f6857d494..08ed23f4a 100644 --- a/src/test/java/com/faforever/api/deployment/ExeUploaderControllerTest.java +++ b/src/test/java/com/faforever/api/deployment/ExeUploaderControllerTest.java @@ -2,15 +2,15 @@ import com.faforever.api.config.FafApiProperties; import com.faforever.api.config.TestWebSecurityConfig; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import org.springframework.mock.web.MockMultipartFile; -import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.web.servlet.MockMvc; import javax.inject.Inject; @@ -19,7 +19,7 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.fileUpload; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -@RunWith(SpringRunner.class) +@ExtendWith(SpringExtension.class) @WebMvcTest(ExeUploaderController.class) @Import(TestWebSecurityConfig.class) public class ExeUploaderControllerTest { @@ -41,7 +41,7 @@ public void init(MockMvc mvc) { this.mvc = mvc; } - @Before + @BeforeEach public void setUp() { when(fafApiProperties.getDeployment()).thenReturn(deployment); when(deployment.getAllowedExeExtension()).thenReturn("exe"); diff --git a/src/test/java/com/faforever/api/deployment/ExeUploaderServiceTest.java b/src/test/java/com/faforever/api/deployment/ExeUploaderServiceTest.java index 34fe93c91..974511948 100644 --- a/src/test/java/com/faforever/api/deployment/ExeUploaderServiceTest.java +++ b/src/test/java/com/faforever/api/deployment/ExeUploaderServiceTest.java @@ -5,20 +5,22 @@ import com.faforever.api.error.ApiException; import com.faforever.api.featuredmods.FeaturedModFile; import com.faforever.api.featuredmods.FeaturedModService; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; -import org.junit.rules.TemporaryFolder; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.io.TempDir; import org.mockito.ArgumentCaptor; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.junit.jupiter.MockitoExtension; import java.io.ByteArrayInputStream; +import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; @@ -26,16 +28,18 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.is; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) public class ExeUploaderServiceTest { private ExeUploaderService instance; - @Rule - public final TemporaryFolder temporaryDirectory = new TemporaryFolder(); - @Rule - public final TemporaryFolder finalDirectory = new TemporaryFolder(); + private Path temporaryDirectory; + private Path finalDirectory; @Mock private ContentService contentService; @@ -47,16 +51,12 @@ public class ExeUploaderServiceTest { private FeaturedModService featuredModService; private InputStream exeDataInputStream; - private FeaturedModFile featuredModFile; - @Before - public void setUp() { + @BeforeEach + public void setUp() throws IOException { instance = new ExeUploaderService(contentService, apiProperties, featuredModService); - - when(apiProperties.getDeployment()).thenReturn(deployment); - exeDataInputStream = new ByteArrayInputStream(new byte[] {1, 2, 3, 4}); - when(contentService.createTempDir()).thenReturn(temporaryDirectory.getRoot().toPath()); + exeDataInputStream = new ByteArrayInputStream(new byte[]{1, 2, 3, 4}); featuredModFile = new FeaturedModFile(); featuredModFile.setName("ForgedAlliance.exe"); @@ -64,55 +64,71 @@ public void setUp() { featuredModFile.setFileId((short) 1); } - @After - public void after() { - verifyNoMoreInteractions(contentService, apiProperties, deployment, featuredModService); - } - @Test - public void testProcessUploadBeta() { - String modName = "fafbeta"; - - String finalExeDestination = finalDirectory.getRoot().getAbsolutePath() + "/ForgedAlliance.1.exe"; - when(deployment.getForgedAllianceBetaExePath()).thenReturn(finalExeDestination); - - when(featuredModService.getFile(modName, null, "ForgedAlliance.exe")).thenReturn( - featuredModFile); - instance.processUpload(exeDataInputStream, modName); - - assertTrue(Files.exists(Paths.get(finalExeDestination))); - ArgumentCaptor> modFilesCaptor = ArgumentCaptor.forClass(List.class); - verify(featuredModService).save(eq(modName), eq((short) featuredModFile.getVersion()), modFilesCaptor.capture()); - assertThat(modFilesCaptor.getValue().size(), is(1)); - assertThat(modFilesCaptor.getValue(), hasItem(featuredModFile)); - - verify(contentService).createTempDir(); - verify(apiProperties, atLeastOnce()).getDeployment(); - verify(deployment).getForgedAllianceBetaExePath(); - verify(featuredModService).getFile(modName, null, "ForgedAlliance.exe"); - } + @Nested + class WithTempDir { + @TempDir + public Path baseTemporaryDirectory; - @Test - public void testProcessUploadDevelop() { - String modName = "fafdevelop"; - - String finalExeDestination = finalDirectory.getRoot().getAbsolutePath() + "/ForgedAlliance.1.exe"; - when(deployment.getForgedAllianceDevelopExePath()).thenReturn(finalExeDestination); - - when(featuredModService.getFile(modName, null, "ForgedAlliance.exe")).thenReturn( - featuredModFile); - instance.processUpload(exeDataInputStream, modName); - - assertTrue(Files.exists(Paths.get(finalExeDestination))); - ArgumentCaptor> modFilesCaptor = ArgumentCaptor.forClass(List.class); - verify(featuredModService).save(eq(modName), eq((short) featuredModFile.getVersion()), modFilesCaptor.capture()); - assertThat(modFilesCaptor.getValue().size(), is(1)); - assertThat(modFilesCaptor.getValue(), hasItem(featuredModFile)); - - verify(contentService).createTempDir(); - verify(apiProperties, atLeastOnce()).getDeployment(); - verify(deployment).getForgedAllianceDevelopExePath(); - verify(featuredModService).getFile(modName, null, "ForgedAlliance.exe"); + @BeforeEach + public void setUp() throws IOException { + temporaryDirectory = Files.createDirectory(baseTemporaryDirectory.resolve("temp")); + finalDirectory = Files.createDirectory(baseTemporaryDirectory.resolve("final")); + + when(apiProperties.getDeployment()).thenReturn(deployment); + when(contentService.createTempDir()).thenReturn(temporaryDirectory); + } + + @AfterEach + public void after() { + verifyNoMoreInteractions(contentService, apiProperties, deployment, featuredModService); + } + + @Test + public void testProcessUploadBeta() { + String modName = "fafbeta"; + + String finalExeDestination = finalDirectory.toAbsolutePath() + "/ForgedAlliance.1.exe"; + when(deployment.getForgedAllianceBetaExePath()).thenReturn(finalExeDestination); + + when(featuredModService.getFile(modName, null, "ForgedAlliance.exe")).thenReturn( + featuredModFile); + instance.processUpload(exeDataInputStream, modName); + + assertTrue(Files.exists(Paths.get(finalExeDestination))); + ArgumentCaptor> modFilesCaptor = ArgumentCaptor.forClass(List.class); + verify(featuredModService).save(eq(modName), eq((short) featuredModFile.getVersion()), modFilesCaptor.capture()); + assertThat(modFilesCaptor.getValue().size(), is(1)); + assertThat(modFilesCaptor.getValue(), hasItem(featuredModFile)); + + verify(contentService).createTempDir(); + verify(apiProperties, atLeastOnce()).getDeployment(); + verify(deployment).getForgedAllianceBetaExePath(); + verify(featuredModService).getFile(modName, null, "ForgedAlliance.exe"); + } + + @Test + public void testProcessUploadDevelop() { + String modName = "fafdevelop"; + + String finalExeDestination = finalDirectory.toAbsolutePath() + "/ForgedAlliance.1.exe"; + when(deployment.getForgedAllianceDevelopExePath()).thenReturn(finalExeDestination); + + when(featuredModService.getFile(modName, null, "ForgedAlliance.exe")).thenReturn( + featuredModFile); + instance.processUpload(exeDataInputStream, modName); + + assertTrue(Files.exists(Paths.get(finalExeDestination))); + ArgumentCaptor> modFilesCaptor = ArgumentCaptor.forClass(List.class); + verify(featuredModService).save(eq(modName), eq((short) featuredModFile.getVersion()), modFilesCaptor.capture()); + assertThat(modFilesCaptor.getValue().size(), is(1)); + assertThat(modFilesCaptor.getValue(), hasItem(featuredModFile)); + + verify(contentService).createTempDir(); + verify(apiProperties, atLeastOnce()).getDeployment(); + verify(deployment).getForgedAllianceDevelopExePath(); + verify(featuredModService).getFile(modName, null, "ForgedAlliance.exe"); + } } @Test From 011eae84d2b13acb11f5dadf0975e61ea78bdfb5 Mon Sep 17 00:00:00 2001 From: Konstantin Kalinovsky Date: Tue, 24 Sep 2019 18:08:50 +0200 Subject: [PATCH 10/10] Minor code changes --- .../api/deployment/ExeUploadControllerTest.java | 1 - .../faforever/api/deployment/ExeUploaderService.java | 10 ++++------ .../featuredmods/LegacyFeaturedModFileRepository.java | 5 ++++- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/inttest/java/com/faforever/api/deployment/ExeUploadControllerTest.java b/src/inttest/java/com/faforever/api/deployment/ExeUploadControllerTest.java index 029b4b156..1424a12b7 100644 --- a/src/inttest/java/com/faforever/api/deployment/ExeUploadControllerTest.java +++ b/src/inttest/java/com/faforever/api/deployment/ExeUploadControllerTest.java @@ -2,7 +2,6 @@ import com.faforever.api.AbstractIntegrationTest; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.springframework.mock.web.MockMultipartFile; import org.springframework.test.context.jdbc.Sql; diff --git a/src/main/java/com/faforever/api/deployment/ExeUploaderService.java b/src/main/java/com/faforever/api/deployment/ExeUploaderService.java index 8fcf1cf47..0496c920b 100644 --- a/src/main/java/com/faforever/api/deployment/ExeUploaderService.java +++ b/src/main/java/com/faforever/api/deployment/ExeUploaderService.java @@ -14,7 +14,7 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.util.Assert; -import java.io.File; +import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; @@ -24,8 +24,7 @@ import java.util.List; import static com.google.common.hash.Hashing.md5; -import static com.google.common.io.Files.hash; -import static java.nio.file.Files.copy; +import static com.google.common.io.Files.asByteSource; import static java.nio.file.Files.createDirectories; @Service @@ -56,14 +55,13 @@ public void processUpload(InputStream exeDataInputStream, String modName) { featuredModFile.getName(), modName ); - featuredModFile.setMd5(hash(uploadedFile.toFile(), md5()).toString()); + featuredModFile.setMd5(asByteSource(uploadedFile.toFile()).hash(md5()).toString()); exeDataInputStream.close(); List featuredModFiles = Collections.singletonList(featuredModFile); featuredModService.save(modName, (short) featuredModFile.getVersion(), featuredModFiles); } - @SneakyThrows - public Path upload(InputStream exeDataInputStream, String fileName, String modName) { + private Path upload(InputStream exeDataInputStream, String fileName, String modName) throws IOException { Assert.isTrue(exeDataInputStream.available() > 0, "data of 'ForgedAlliance.exe' must not be empty"); Path tempDir = contentService.createTempDir(); diff --git a/src/main/java/com/faforever/api/featuredmods/LegacyFeaturedModFileRepository.java b/src/main/java/com/faforever/api/featuredmods/LegacyFeaturedModFileRepository.java index 83c6bbaf6..9c5a20f7f 100644 --- a/src/main/java/com/faforever/api/featuredmods/LegacyFeaturedModFileRepository.java +++ b/src/main/java/com/faforever/api/featuredmods/LegacyFeaturedModFileRepository.java @@ -21,7 +21,10 @@ public LegacyFeaturedModFileRepository(EntityManager entityManager) { public FeaturedModFile getFile(String modName, Integer version, String fileName) { List files = getFiles(modName, version, fileName); - Assert.state(files.size() > 0); + Assert.isTrue( + files.size() == 1, + String.format("Unexpected result size %d for modName: %s, version: %d, filename: %s", files.size(), modName, version, fileName) + ); return files.get(0); }