This commit is contained in:
Alessandro Autiero
2024-06-01 16:26:00 +02:00
parent d478650e9b
commit efb508bd0c
243 changed files with 486662 additions and 2948 deletions

View File

@@ -52,7 +52,7 @@ void main(List<String> args) async {
} }
stdout.writeln("Launching game..."); stdout.writeln("Launching game...");
var executable = await version.executable; var executable = version.gameExecutable;
if(executable == null){ if(executable == null){
throw Exception("Missing game executable at: ${version.location.path}"); throw Exception("Missing game executable at: ${version.location.path}");
} }

View File

@@ -12,7 +12,7 @@ Future<void> startGame() async {
await _startLauncherProcess(version); await _startLauncherProcess(version);
await _startEacProcess(version); await _startEacProcess(version);
var executable = await version.executable; var executable = await version.gameExecutable;
if (executable == null) { if (executable == null) {
throw Exception("${version.location.path} no longer contains a Fortnite executable, did you delete or move it?"); throw Exception("${version.location.path} no longer contains a Fortnite executable, did you delete or move it?");
} }

View File

@@ -42,7 +42,7 @@ Future<void> downloadRequiredDLLs() async {
await memoryFixDll.writeAsBytes(response.bodyBytes); await memoryFixDll.writeAsBytes(response.bodyBytes);
} }
if(!authenticatorDirectory.existsSync()){ if(!backendDirectory.existsSync()){
var response = await http.get(Uri.parse(_embeddedConfigDownload)); var response = await http.get(Uri.parse(_embeddedConfigDownload));
if(response.statusCode != 200){ if(response.statusCode != 200){
throw Exception("Cannot download embedded server config"); throw Exception("Cannot download embedded server config");
@@ -50,6 +50,6 @@ Future<void> downloadRequiredDLLs() async {
var tempZip = File("${tempDirectory.path}/reboot_config.zip"); var tempZip = File("${tempDirectory.path}/reboot_config.zip");
await tempZip.writeAsBytes(response.bodyBytes); await tempZip.writeAsBytes(response.bodyBytes);
await extractFileToDisk(tempZip.path, authenticatorDirectory.path); await extractFileToDisk(tempZip.path, backendDirectory.path);
} }
} }

View File

@@ -1,13 +1,13 @@
import 'dart:io'; import 'dart:io';
import 'package:reboot_common/common.dart'; import 'package:reboot_common/common.dart';
import 'package:reboot_common/src/util/authenticator.dart' as server; import 'package:reboot_common/src/util/backend.dart' as server;
Future<bool> startServerCli(String? host, int? port, ServerType type) async { Future<bool> startServerCli(String? host, int? port, ServerType type) async {
stdout.writeln("Starting backend server..."); stdout.writeln("Starting backend server...");
switch(type){ switch(type){
case ServerType.local: case ServerType.local:
var result = await pingAuthenticator(host ?? kDefaultAuthenticatorHost, port ?? kDefaultAuthenticatorPort); var result = await pingBackend(host ?? kDefaultBackendHost, port ?? kDefaultBackendPort);
if(result == null){ if(result == null){
throw Exception("Local backend server is not running"); throw Exception("Local backend server is not running");
} }
@@ -16,8 +16,8 @@ Future<bool> startServerCli(String? host, int? port, ServerType type) async {
return true; return true;
case ServerType.embedded: case ServerType.embedded:
stdout.writeln("Starting an embedded server..."); stdout.writeln("Starting an embedded server...");
await server.startEmbeddedAuthenticator(false); await server.startEmbeddedBackend(false);
var result = await pingAuthenticator(host ?? kDefaultAuthenticatorHost, port ?? kDefaultAuthenticatorPort); var result = await pingBackend(host ?? kDefaultBackendHost, port ?? kDefaultBackendPort);
if(result == null){ if(result == null){
throw Exception("Cannot start embedded server"); throw Exception("Cannot start embedded server");
} }
@@ -39,12 +39,12 @@ Future<bool> startServerCli(String? host, int? port, ServerType type) async {
Future<HttpServer?> _changeReverseProxyState(String host, int port) async { Future<HttpServer?> _changeReverseProxyState(String host, int port) async {
try{ try{
var uri = await pingAuthenticator(host, port); var uri = await pingBackend(host, port);
if(uri == null){ if(uri == null){
return null; return null;
} }
return await server.startRemoteAuthenticatorProxy(uri); return await server.startRemoteBackendProxy(uri);
}catch(error){ }catch(error){
throw Exception("Cannot start reverse proxy"); throw Exception("Cannot start reverse proxy");
} }

View File

@@ -1,4 +1,4 @@
export 'package:reboot_common/src/constant/authenticator.dart'; export 'package:reboot_common/src/constant/backend.dart';
export 'package:reboot_common/src/constant/game.dart'; export 'package:reboot_common/src/constant/game.dart';
export 'package:reboot_common/src/constant/matchmaker.dart'; export 'package:reboot_common/src/constant/matchmaker.dart';
export 'package:reboot_common/src/constant/supabase.dart'; export 'package:reboot_common/src/constant/supabase.dart';
@@ -9,8 +9,7 @@ export 'package:reboot_common/src/model/server_result.dart';
export 'package:reboot_common/src/model/server_type.dart'; export 'package:reboot_common/src/model/server_type.dart';
export 'package:reboot_common/src/model/update_status.dart'; export 'package:reboot_common/src/model/update_status.dart';
export 'package:reboot_common/src/model/update_timer.dart'; export 'package:reboot_common/src/model/update_timer.dart';
export 'package:reboot_common/src/model/process.dart'; export 'package:reboot_common/src/util/backend.dart';
export 'package:reboot_common/src/util/authenticator.dart';
export 'package:reboot_common/src/util/build.dart'; export 'package:reboot_common/src/util/build.dart';
export 'package:reboot_common/src/util/dll.dart'; export 'package:reboot_common/src/util/dll.dart';
export 'package:reboot_common/src/util/matchmaker.dart'; export 'package:reboot_common/src/util/matchmaker.dart';

View File

@@ -1,2 +0,0 @@
const String kDefaultAuthenticatorHost = "127.0.0.1";
const int kDefaultAuthenticatorPort = 3551;

View File

@@ -0,0 +1,2 @@
const String kDefaultBackendHost = "127.0.0.1";
const int kDefaultBackendPort = 3551;

View File

@@ -1,11 +1,17 @@
const String kDefaultPlayerName = "Player"; const String kDefaultPlayerName = "Player";
const String kDefaultGameServerHost = "127.0.0.1"; const String kDefaultGameServerHost = "127.0.0.1";
const String kDefaultGameServerPort = "7777"; const String kDefaultGameServerPort = "7777";
const String kConsoleLine = "Region "; const String kInitializedLine = "Game Engine Initialized";
const List<String> kLoggedInLines = [
"[UOnlineAccountCommon::ContinueLoggingIn]",
"(Completed)"
];
const String kShutdownLine = "FOnlineSubsystemGoogleCommon::Shutdown()"; const String kShutdownLine = "FOnlineSubsystemGoogleCommon::Shutdown()";
const List<String> kCorruptedBuildErrors = [ const List<String> kCorruptedBuildErrors = [
"Critical error",
"when 0 bytes remain", "when 0 bytes remain",
"Pak chunk signature verification failed!" "Pak chunk signature verification failed!",
"Couldn't find pak signature file"
]; ];
const List<String> kCannotConnectErrors = [ const List<String> kCannotConnectErrors = [
"port 3551 failed: Connection refused", "port 3551 failed: Connection refused",

View File

@@ -20,7 +20,11 @@ class FortniteBuildDownloadProgress {
final int? minutesLeft; final int? minutesLeft;
final bool extracting; final bool extracting;
FortniteBuildDownloadProgress(this.progress, this.minutesLeft, this.extracting); FortniteBuildDownloadProgress({
required this.progress,
required this.extracting,
this.minutesLeft,
});
} }
class FortniteBuildDownloadOptions { class FortniteBuildDownloadOptions {

View File

@@ -6,7 +6,6 @@ class GameInstance {
final int gamePid; final int gamePid;
final int? launcherPid; final int? launcherPid;
final int? eacPid; final int? eacPid;
int? observerPid;
bool hosting; bool hosting;
bool launched; bool launched;
bool tokenError; bool tokenError;
@@ -29,9 +28,18 @@ class GameInstance {
if(eacPid != null) { if(eacPid != null) {
Process.killPid(eacPid!, ProcessSignal.sigabrt); Process.killPid(eacPid!, ProcessSignal.sigabrt);
} }
if(observerPid != null) {
Process.killPid(observerPid!, ProcessSignal.sigabrt);
} }
child?.kill();
bool get nestedHosting {
GameInstance? child = this;
while(child != null) {
if(child.hosting) {
return true;
}
child = child.child;
}
return false;
} }
} }

View File

@@ -1,23 +0,0 @@
class Win32Process {
final int pid;
final Stream<String> stdOutput;
final Stream<String> errorOutput;
Win32Process({
required this.pid,
required this.stdOutput,
required this.errorOutput
});
}
class PrimitiveWin32Process {
final int pid;
final int stdOutputHandle;
final int errorOutputHandle;
PrimitiveWin32Process({
required this.pid,
required this.stdOutputHandle,
required this.errorOutputHandle
});
}

View File

@@ -24,4 +24,6 @@ enum ServerResultType {
pingError; pingError;
bool get isError => name.contains("Error"); bool get isError => name.contains("Error");
bool get isSuccess => this == ServerResultType.startSuccess || this == ServerResultType.stopSuccess;
} }

View File

@@ -1,53 +0,0 @@
import 'dart:io';
import 'package:reboot_common/common.dart';
import 'package:shelf/shelf_io.dart';
import 'package:shelf_proxy/shelf_proxy.dart';
final authenticatorDirectory = Directory("${assetsDirectory.path}\\authenticator");
final authenticatorStartExecutable = File("${authenticatorDirectory.path}\\lawinserver.exe");
Future<Win32Process> startEmbeddedAuthenticator(bool detached) async => startProcess(
executable: authenticatorStartExecutable,
window: detached,
);
Future<HttpServer> startRemoteAuthenticatorProxy(Uri uri) async => await serve(proxyHandler(uri), kDefaultAuthenticatorHost, kDefaultAuthenticatorPort);
Future<bool> isAuthenticatorPortFree() async => await pingAuthenticator(kDefaultAuthenticatorHost, kDefaultAuthenticatorPort) == null;
Future<bool> freeAuthenticatorPort() async {
await killProcessByPort(kDefaultAuthenticatorPort);
final standardResult = await isAuthenticatorPortFree();
if(standardResult) {
return true;
}
return false;
}
Future<Uri?> pingAuthenticator(String host, int port, [bool https=false]) async {
var hostName = _getHostName(host);
var declaredScheme = _getScheme(host);
try{
var uri = Uri(
scheme: declaredScheme ?? (https ? "https" : "http"),
host: hostName,
port: port,
path: "unknown"
);
var client = HttpClient()
..connectionTimeout = const Duration(seconds: 5);
var request = await client.getUrl(uri);
var response = await request.close();
return response.statusCode == 200 || response.statusCode == 404 ? uri : null;
}catch(_){
return https || declaredScheme != null || isLocalHost(host) ? null : await pingAuthenticator(host, port, true);
}
}
String? _getHostName(String host) => host.replaceFirst("http://", "").replaceFirst("https://", "");
String? _getScheme(String host) => host.startsWith("http://") ? "http" : host.startsWith("https://") ? "https" : null;

View File

@@ -1,24 +1,61 @@
import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:ini/ini.dart'; import 'package:ini/ini.dart';
import 'package:reboot_common/common.dart'; import 'package:reboot_common/common.dart';
import 'package:shelf/shelf_io.dart';
import 'package:shelf_proxy/shelf_proxy.dart';
import 'package:sync/semaphore.dart'; import 'package:sync/semaphore.dart';
final matchmakerDirectory = Directory("${assetsDirectory.path}\\matchmaker"); final Directory backendDirectory = Directory("${assetsDirectory.path}\\backend");
final matchmakerStartExecutable = File("${matchmakerDirectory.path}\\fortmatchmaker.exe"); final File backendStartExecutable = File("${backendDirectory.path}\\lawinserver.exe");
final matchmakerKillExecutable = File("${authenticatorDirectory.path}\\kill.bat"); final File matchmakerConfigFile = File("${backendDirectory.path}\\Config\\config.ini");
final matchmakerConfigFile = File("${authenticatorDirectory.path}\\Config\\config.ini"); final Semaphore _semaphore = Semaphore();
String? _lastIp; String? _lastIp;
String? _lastPort; String? _lastPort;
Semaphore _semaphore = Semaphore();
Future<Win32Process> startEmbeddedMatchmaker(bool detached) async => startProcess( Future<Process> startEmbeddedBackend(bool detached) async => startProcess(
executable: matchmakerStartExecutable, executable: backendStartExecutable,
window: detached, window: detached,
); );
Future<HttpServer> startRemoteBackendProxy(Uri uri) async => await serve(proxyHandler(uri), kDefaultBackendHost, kDefaultBackendPort);
Future<bool> isBackendPortFree() async => await pingBackend(kDefaultBackendHost, kDefaultBackendPort) == null;
Future<bool> freeBackendPort() async {
await killProcessByPort(kDefaultBackendPort);
final standardResult = await isBackendPortFree();
if(standardResult) {
return true;
}
return false;
}
Future<Uri?> pingBackend(String host, int port, [bool https=false]) async {
var hostName = _getHostName(host);
var declaredScheme = _getScheme(host);
try{
var uri = Uri(
scheme: declaredScheme ?? (https ? "https" : "http"),
host: hostName,
port: port,
path: "unknown"
);
var client = HttpClient()
..connectionTimeout = const Duration(seconds: 5);
var request = await client.getUrl(uri);
var response = await request.close();
return response.statusCode == 200 || response.statusCode == 404 ? uri : null;
}catch(_){
return https || declaredScheme != null || isLocalHost(host) ? null : await pingBackend(host, port, true);
}
}
String? _getHostName(String host) => host.replaceFirst("http://", "").replaceFirst("https://", "");
String? _getScheme(String host) => host.startsWith("http://") ? "http" : host.startsWith("https://") ? "https" : null;
Stream<String?> watchMatchmakingIp() async* { Stream<String?> watchMatchmakingIp() async* {
if(!matchmakerConfigFile.existsSync()){ if(!matchmakerConfigFile.existsSync()){
return; return;
@@ -126,19 +163,3 @@ Future<Uri?> pingMatchmaker(String host, int port, [bool wss=false]) async {
String? _getHostName(String host) => host.replaceFirst("ws://", "").replaceFirst("wss://", ""); String? _getHostName(String host) => host.replaceFirst("ws://", "").replaceFirst("wss://", "");
String? _getScheme(String host) => host.startsWith("ws://") ? "ws" : host.startsWith("wss://") ? "wss" : null; String? _getScheme(String host) => host.startsWith("ws://") ? "ws" : host.startsWith("wss://") ? "wss" : null;
extension StringExtension on String {
bool get isBlank {
if(isEmpty) {
return true;
}
for(var char in this.split("")) {
if(char != " ") {
return false;
}
}
return true;
}
}

View File

@@ -25,8 +25,11 @@ Dio _buildDioInstance() {
final String _archiveSourceUrl = "https://raw.githubusercontent.com/simplyblk/Fortnitebuilds/main/README.md"; final String _archiveSourceUrl = "https://raw.githubusercontent.com/simplyblk/Fortnitebuilds/main/README.md";
final RegExp _rarProgressRegex = RegExp("^((100)|(\\d{1,2}(.\\d*)?))%\$"); final RegExp _rarProgressRegex = RegExp("^((100)|(\\d{1,2}(.\\d*)?))%\$");
const String _manifestSourceUrl = "https://manifest.fnbuilds.services"; const String _manifestSourceUrl = "http://manifest.simplyblk.xyz";
const int _maxDownloadErrors = 30; const String _deniedConnectionError = "The connection was denied: your firewall might be blocking the download";
const String _unavailableError = "The build downloader is not available right now";
const String _genericError = "The build downloader is not working correctly";
const int _maxErrors = 100;
Future<List<FortniteBuild>> fetchBuilds(ignored) async { Future<List<FortniteBuild>> fetchBuilds(ignored) async {
(_dio.httpClientAdapter as IOHttpClientAdapter).createHttpClient = () => (_dio.httpClientAdapter as IOHttpClientAdapter).createHttpClient = () =>
@@ -45,7 +48,15 @@ Future<List<FortniteBuild>> fetchBuilds(ignored) async {
Future<List<FortniteBuild>> _fetchManifestBuilds() async { Future<List<FortniteBuild>> _fetchManifestBuilds() async {
try { try {
final response = await _dio.get<String>("$_manifestSourceUrl/versions.json"); final response = await _dio.get<String>(
"$_manifestSourceUrl/versions.json",
options: Options(
headers: {
"Accept-Encoding": "*",
"Cookie": "_c_t_c=1"
}
)
);
final body = response.data; final body = response.data;
return jsonDecode(body!).map((version) { return jsonDecode(body!).map((version) {
final nameParts = version.split("-"); final nameParts = version.split("-");
@@ -131,7 +142,14 @@ Future<void> downloadArchiveBuild(FortniteBuildDownloadOptions options) async {
delete(outputDir); delete(outputDir);
break; break;
case FortniteBuildSource.manifest: case FortniteBuildSource.manifest:
final response = await _dio.get<String>(options.build.link); final response = await _dio.get<String>(
options.build.link,
options: Options(
headers: {
"Cookie": "_c_t_c=1"
}
)
);
final manifest = FortniteBuildManifestFile.fromJson(jsonDecode(response.data!)); final manifest = FortniteBuildManifestFile.fromJson(jsonDecode(response.data!));
final totalBytes = manifest.size; final totalBytes = manifest.size;
@@ -171,7 +189,8 @@ Future<void> downloadArchiveBuild(FortniteBuildDownloadOptions options) async {
options: Options( options: Options(
responseType: ResponseType.bytes, responseType: ResponseType.bytes,
headers: { headers: {
"Accept-Encoding": "gzip" "Accept-Encoding": "gzip",
"Cookie": "_c_t_c=1"
} }
), ),
); );
@@ -201,24 +220,23 @@ Future<void> downloadArchiveBuild(FortniteBuildDownloadOptions options) async {
} }
}); });
await Future.any([stopped.future, Future.wait(writers)]); await Future.any([stopped.future, Future.wait(writers)]);
options.port.send(FortniteBuildDownloadProgress(100, 0, true));
break; break;
} }
}catch(error, stackTrace) { }catch(error) {
options.port.send("$error\n$stackTrace"); _onError(error, options);
} }
} }
Future<Response> _downloadArchive(FortniteBuildDownloadOptions options, File tempFile, int startTime, [int? byteStart = null, int errorsCount = 0]) async { Future<void> _downloadArchive(FortniteBuildDownloadOptions options, File tempFile, int startTime, [int? byteStart = null, int errorsCount = 0]) async {
var received = byteStart ?? 0; var received = byteStart ?? 0;
try { try {
return await _dio.download( await _dio.download(
options.build.link, options.build.link,
tempFile.path, tempFile.path,
onReceiveProgress: (data, length) { onReceiveProgress: (data, length) {
received = data; received = data;
final percentage = (received / length) * 100; final percentage = (received / length) * 100;
_onProgress(startTime, DateTime.now().millisecondsSinceEpoch, percentage, false, options); _onProgress(startTime, percentage < 1 ? null : DateTime.now().millisecondsSinceEpoch, percentage, false, options);
}, },
deleteOnError: false, deleteOnError: false,
options: Options( options: Options(
@@ -228,10 +246,14 @@ Future<Response> _downloadArchive(FortniteBuildDownloadOptions options, File tem
} }
if(statusCode == 403 || statusCode == 503) { if(statusCode == 403 || statusCode == 503) {
throw Exception("The connection was denied: your firewall might be blocking the download"); throw _deniedConnectionError;
} }
throw Exception("The build downloader is not available right now"); if(statusCode == 404) {
throw _unavailableError;
}
throw _genericError;
}, },
headers: byteStart == null || byteStart <= 0 ? { headers: byteStart == null || byteStart <= 0 ? {
"Cookie": "_c_t_c=1" "Cookie": "_c_t_c=1"
@@ -242,17 +264,18 @@ Future<Response> _downloadArchive(FortniteBuildDownloadOptions options, File tem
) )
); );
}catch(error) { }catch(error) {
if(errorsCount >= _maxDownloadErrors) { if(errorsCount > _maxErrors || error.toString().contains(_deniedConnectionError) || error.toString().contains(_unavailableError)) {
throw error; _onError(error, options);
return;
} }
return await _downloadArchive(options, tempFile, startTime, received, errorsCount + 1); await _downloadArchive(options, tempFile, startTime, received, errorsCount + 1);
} }
} }
Future<void> _extractArchive(Completer<dynamic> stopped, String extension, File tempFile, FortniteBuildDownloadOptions options) async { Future<void> _extractArchive(Completer<dynamic> stopped, String extension, File tempFile, FortniteBuildDownloadOptions options) async {
final startTime = DateTime.now().millisecondsSinceEpoch; final startTime = DateTime.now().millisecondsSinceEpoch;
Win32Process? process; Process? process;
switch (extension.toLowerCase()) { switch (extension.toLowerCase()) {
case ".zip": case ".zip":
final sevenZip = File("${assetsDirectory.path}\\build\\7zip.exe"); final sevenZip = File("${assetsDirectory.path}\\build\\7zip.exe");
@@ -271,10 +294,10 @@ Future<void> _extractArchive(Completer<dynamic> stopped, String extension, File
], ],
); );
process.stdOutput.listen((data) { process.stdOutput.listen((data) {
print(data);
final now = DateTime.now().millisecondsSinceEpoch; final now = DateTime.now().millisecondsSinceEpoch;
if(data == "Everything is Ok") { if(data.toLowerCase().contains("everything is ok")) {
options.port.send(FortniteBuildDownloadProgress(100, 0, true)); _onProgress(startTime, now, 100, true, options);
process?.kill(ProcessSignal.sigabrt);
return; return;
} }
@@ -286,6 +309,11 @@ Future<void> _extractArchive(Completer<dynamic> stopped, String extension, File
final percentage = int.parse(element.substring(0, element.length - 1)).toDouble(); final percentage = int.parse(element.substring(0, element.length - 1)).toDouble();
_onProgress(startTime, now, percentage, true, options); _onProgress(startTime, now, percentage, true, options);
}); });
process.stdError.listen((data) {
if(!data.isBlank) {
_onError(data, options);
}
});
break; break;
case ".rar": case ".rar":
final winrar = File("${assetsDirectory.path}\\build\\winrar.exe"); final winrar = File("${assetsDirectory.path}\\build\\winrar.exe");
@@ -298,17 +326,17 @@ Future<void> _extractArchive(Completer<dynamic> stopped, String extension, File
args: [ args: [
"x", "x",
"-o+", "-o+",
tempFile.path, '"${tempFile.path}"',
"*.*", "*.*",
options.destination.path '"${options.destination.path}"'
] ]
); );
process.stdOutput.listen((data) { process.stdOutput.listen((data) {
print(data);
final now = DateTime.now().millisecondsSinceEpoch; final now = DateTime.now().millisecondsSinceEpoch;
data.replaceAll("\r", "").replaceAll("\b", "").trim(); data = data.replaceAll("\r", "").replaceAll("\b", "").trim();
if(data == "All OK") { if(data == "All OK") {
options.port.send(FortniteBuildDownloadProgress(100, 0, true)); _onProgress(startTime, now, 100, true, options);
process?.kill(ProcessSignal.sigabrt);
return; return;
} }
@@ -320,7 +348,11 @@ Future<void> _extractArchive(Completer<dynamic> stopped, String extension, File
final percentage = int.parse(element).toDouble(); final percentage = int.parse(element).toDouble();
_onProgress(startTime, now, percentage, true, options); _onProgress(startTime, now, percentage, true, options);
}); });
process.errorOutput.listen((data) => options.port.send(data)); process.stdError.listen((data) {
if(!data.isBlank) {
_onError(data, options);
}
});
break; break;
default: default:
throw ArgumentError("Unexpected file extension: $extension}"); throw ArgumentError("Unexpected file extension: $extension}");
@@ -329,17 +361,31 @@ Future<void> _extractArchive(Completer<dynamic> stopped, String extension, File
await Future.any([stopped.future, watchProcess(process.pid)]); await Future.any([stopped.future, watchProcess(process.pid)]);
} }
void _onProgress(int startTime, int now, double percentage, bool extracting, FortniteBuildDownloadOptions options) { void _onProgress(int startTime, int? now, double percentage, bool extracting, FortniteBuildDownloadOptions options) {
if(percentage == 0) { if(percentage == 0) {
options.port.send(FortniteBuildDownloadProgress(percentage, null, extracting)); options.port.send(FortniteBuildDownloadProgress(
progress: percentage,
extracting: extracting
));
return; return;
} }
final msLeft = startTime + (now - startTime) * 100 / percentage - now; final msLeft = now == null ? null : startTime + (now - startTime) * 100 / percentage - now;
final minutesLeft = (msLeft / 1000 / 60).round(); final minutesLeft = msLeft == null ? null : (msLeft / 1000 / 60).round();
options.port.send(FortniteBuildDownloadProgress(percentage, minutesLeft, extracting)); options.port.send(FortniteBuildDownloadProgress(
progress: percentage,
extracting: extracting,
minutesLeft: minutesLeft
));
} }
void _onError(Object? error, FortniteBuildDownloadOptions options) {
if(error != null) {
options.port.send(error.toString());
}
}
Completer<dynamic> _setupLifecycle(FortniteBuildDownloadOptions options) { Completer<dynamic> _setupLifecycle(FortniteBuildDownloadOptions options) {
var stopped = Completer(); var stopped = Completer();
var lifecyclePort = ReceivePort(); var lifecyclePort = ReceivePort();

View File

@@ -8,7 +8,7 @@ import 'package:reboot_common/common.dart';
bool _watcher = false; bool _watcher = false;
final File rebootDllFile = File("${assetsDirectory.path}\\dlls\\reboot.dll"); final File rebootDllFile = File("${assetsDirectory.path}\\dlls\\reboot.dll");
const String kRebootDownloadUrl = const String kRebootDownloadUrl =
"https://nightly.link/Milxnor/Project-Reboot-3.0/workflows/msbuild/master/Release.zip"; "http://nightly.link/Milxnor/Project-Reboot-3.0/workflows/msbuild/master/Release.zip";
Future<bool> hasRebootDllUpdate(int? lastUpdateMs, {int hours = 24, bool force = false}) async { Future<bool> hasRebootDllUpdate(int? lastUpdateMs, {int hours = 24, bool force = false}) async {
final lastUpdate = await _getLastUpdate(lastUpdateMs); final lastUpdate = await _getLastUpdate(lastUpdateMs);
@@ -18,7 +18,7 @@ Future<bool> hasRebootDllUpdate(int? lastUpdateMs, {int hours = 24, bool force =
} }
Future<void> downloadCriticalDll(String name, String outputPath) async { Future<void> downloadCriticalDll(String name, String outputPath) async {
final response = await http.get(Uri.parse("https://github.com/Auties00/reboot_launcher/tree/master/gui/assets/dlls/$name")); final response = await http.get(Uri.parse("https://github.com/Auties00/reboot_launcher/raw/master/gui/assets/dlls/$name"));
if(response.statusCode != 200) { if(response.statusCode != 200) {
throw Exception("Cannot download $name: status code ${response.statusCode}"); throw Exception("Cannot download $name: status code ${response.statusCode}");
} }
@@ -64,7 +64,6 @@ Stream<String> watchDlls() async* {
_watcher = true; _watcher = true;
await for(final event in rebootDllFile.parent.watch(events: FileSystemEvent.delete | FileSystemEvent.move)) { await for(final event in rebootDllFile.parent.watch(events: FileSystemEvent.delete | FileSystemEvent.move)) {
print(event);
if (event.path.endsWith(".dll")) { if (event.path.endsWith(".dll")) {
yield event.path; yield event.path;
} }

View File

@@ -31,27 +31,36 @@ Future<bool> _patch(File file, Uint8List original, Uint8List patched) async {
var read = await file.readAsBytes(); var read = await file.readAsBytes();
var length = await file.length(); var length = await file.length();
var offset = 0; var readOffset = 0;
var counter = 0; var patchOffset = -1;
while(offset < length){ var patchCount = 0;
if(read[offset] == original[counter]){ while(readOffset < length){
counter++; if(read[readOffset] == original[patchCount]){
if(patchOffset == -1) {
patchOffset = readOffset;
}
if(++patchCount == original.length) {
break;
}
}else { }else {
counter = 0; patchOffset = -1;
} }
offset++; readOffset++;
if(counter == original.length){
for(var index = 0; index < patched.length; index++){
read[offset - counter + index] = patched[index];
}
await file.writeAsBytes(read, mode: FileMode.write);
return true;
}
} }
print("Offset: $patchOffset");
if(patchOffset == -1) {
return false; return false;
}
for(var i = 0; i < patched.length; i++) {
read[patchOffset + i] = patched[i];
}
await file.writeAsBytes(read, flush: true);
return true;
}catch(_){ }catch(_){
return false; return false;
} }

View File

@@ -52,8 +52,10 @@ extension FortniteVersionExtension on FortniteVersion {
} }
} }
Future<File?> get executable async { File? get gameExecutable => findExecutable(location, "FortniteClient-Win64-Shipping.exe");
var result = findExecutable(location, "FortniteClient-Win64-Shipping-Reboot.exe");
Future<File?> get headlessGameExecutable async {
var result = findExecutable(location, "FortniteClient-Win64-Shipping-Headless.exe");
if(result != null) { if(result != null) {
return result; return result;
} }
@@ -63,12 +65,9 @@ extension FortniteVersionExtension on FortniteVersion {
return null; return null;
} }
var output = File("${original.parent.path}\\FortniteClient-Win64-Shipping-Reboot.exe"); var output = File("${original.parent.path}\\FortniteClient-Win64-Shipping-Headless.exe");
await original.copy(output.path); await original.copy(output.path);
await Future.wait([ await Isolate.run(() => patchHeadless(output));
Isolate.run(() => patchMatchmaking(output)),
Isolate.run(() => patchHeadless(output)),
]);
return output; return output;
} }

View File

@@ -6,13 +6,13 @@ import 'dart:ffi';
import 'dart:io'; import 'dart:io';
import 'dart:isolate'; import 'dart:isolate';
import 'dart:math'; import 'dart:math';
import 'package:path/path.dart' as path;
import 'package:ffi/ffi.dart'; import 'package:ffi/ffi.dart';
import 'package:reboot_common/src/model/process.dart'; import 'package:reboot_common/common.dart';
import 'package:sync/semaphore.dart';
import 'package:win32/win32.dart'; import 'package:win32/win32.dart';
import '../constant/game.dart';
final _ntdll = DynamicLibrary.open('ntdll.dll'); final _ntdll = DynamicLibrary.open('ntdll.dll');
final _kernel32 = DynamicLibrary.open('kernel32.dll'); final _kernel32 = DynamicLibrary.open('kernel32.dll');
final _CreateRemoteThread = _kernel32.lookupFunction< final _CreateRemoteThread = _kernel32.lookupFunction<
@@ -90,139 +90,53 @@ Future<void> injectDll(int pid, String dll) async {
} }
} }
void _startProcess(_ProcessParameters params) { Future<Process> startProcess({required File executable, List<String>? args, bool wrapProcess = true, bool window = false, String? name}) async {
final args = params.args; final argsOrEmpty = args ?? [];
final port = params.port; if(wrapProcess) {
final concatenatedArgs = args == null ? "" : " ${args.join(" ")}"; final tempScriptDirectory = await tempDirectory.createTemp("reboot_launcher_process");
final command = params.window ? 'cmd.exe /k ""${params.executable.path}"$concatenatedArgs"' : '"${params.executable.path}"$concatenatedArgs'; final tempScriptFile = File("${tempScriptDirectory.path}/process.bat");
print(command); final command = window ? 'cmd.exe /k ""${executable.path}" ${argsOrEmpty.join(" ")}"' : '"${executable.path}" ${argsOrEmpty.join(" ")}';
final processInfo = calloc<PROCESS_INFORMATION>(); await tempScriptFile.writeAsString(command, flush: true);
final lpStartupInfo = calloc<STARTUPINFO>(); final process = await Process.start(
lpStartupInfo.ref.cb = sizeOf<STARTUPINFO>(); tempScriptFile.path,
lpStartupInfo.ref.dwFlags |= STARTF_USESTDHANDLES; [],
final securityAttributes = calloc<SECURITY_ATTRIBUTES>(); workingDirectory: executable.parent.path,
securityAttributes.ref.nLength = sizeOf<SECURITY_ATTRIBUTES>(); mode: window ? ProcessStartMode.detachedWithStdio : ProcessStartMode.normal,
securityAttributes.ref.bInheritHandle = TRUE; runInShell: window
final hStdOutRead = calloc<HANDLE>();
final hStdOutWrite = calloc<HANDLE>();
final hStdErrRead = calloc<HANDLE>();
final hStdErrWrite = calloc<HANDLE>();
if (CreatePipe(hStdOutRead, hStdOutWrite, securityAttributes, 0) == 0 || CreatePipe(hStdErrRead, hStdErrWrite, securityAttributes, 0) == 0) {
final error = GetLastError();
port.send("Cannot create process pipe: $error");
return;
}
if(SetHandleInformation(hStdOutRead.value, HANDLE_FLAG_INHERIT, 0) == 0 || SetHandleInformation(hStdErrRead.value, HANDLE_FLAG_INHERIT, 0) == 0) {
final error = GetLastError();
port.send("Cannot set process pipe information: $error");
return;
}
lpStartupInfo.ref.hStdOutput = hStdOutWrite.value;
lpStartupInfo.ref.hStdError = hStdErrWrite.value;
final success = CreateProcess(
nullptr,
TEXT(command),
nullptr,
nullptr,
TRUE,
NORMAL_PRIORITY_CLASS | (params.window ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW) | CREATE_NEW_PROCESS_GROUP,
nullptr,
TEXT(params.executable.parent.path),
lpStartupInfo,
processInfo
); );
if (success == 0) { return _withLogger(name, executable, process, window);
final error = GetLastError();
port.send("Cannot start process: $error");
return;
} }
CloseHandle(processInfo.ref.hProcess); final process = await Process.start(
CloseHandle(processInfo.ref.hThread); executable.path,
CloseHandle(hStdOutWrite.value); args ?? [],
CloseHandle(hStdErrWrite.value); workingDirectory: executable.parent.path,
final pid = processInfo.ref.dwProcessId; mode: window ? ProcessStartMode.detachedWithStdio : ProcessStartMode.normal,
free(lpStartupInfo); runInShell: window
free(processInfo);
port.send(PrimitiveWin32Process(
pid: pid,
stdOutputHandle: hStdOutRead.value,
errorOutputHandle: hStdErrRead.value
));
}
class _PipeReaderParams {
final int handle;
final SendPort port;
_PipeReaderParams(this.handle, this.port);
}
void _pipeToStreamChannelled(_PipeReaderParams params) {
final buf = calloc<Uint8>(chunkSize);
while(true) {
final bytesReadPtr = calloc<Uint32>();
final success = ReadFile(params.handle, buf, chunkSize, bytesReadPtr, nullptr);
if (success == FALSE) {
break;
}
final bytesRead = bytesReadPtr.value;
if (bytesRead == 0) {
break;
}
final lines = utf8.decode(buf.asTypedList(bytesRead)).split('\n');
for(final line in lines) {
params.port.send(line);
}
}
CloseHandle(params.handle);
free(buf);
Isolate.current.kill();
}
Stream<String> _pipeToStream(int pipeHandle) {
final port = ReceivePort();
Isolate.spawn(
_pipeToStreamChannelled,
_PipeReaderParams(pipeHandle, port.sendPort)
); );
return port.map((event) => event as String); return _withLogger(name, executable, process, window);
} }
class _ProcessParameters { _ExtendedProcess _withLogger(String? name, File executable, Process process, bool window) {
File executable; final extendedProcess = _ExtendedProcess(process, true);
List<String>? args; final loggingFile = File("${logsDirectory.path}\\${name ?? path.basenameWithoutExtension(executable.path)}-${DateTime.now().millisecondsSinceEpoch}.log");
bool window; loggingFile.parent.createSync(recursive: true);
SendPort port; if(loggingFile.existsSync()) {
loggingFile.deleteSync();
_ProcessParameters(this.executable, this.args, this.window, this.port);
}
Future<Win32Process> startProcess({required File executable, List<String>? args, bool output = true, bool window = false}) async {
final completer = Completer<Win32Process>();
final port = ReceivePort();
port.listen((message) {
if(message is PrimitiveWin32Process) {
completer.complete(Win32Process(
pid: message.pid,
stdOutput: _pipeToStream(message.stdOutputHandle),
errorOutput: _pipeToStream(message.errorOutputHandle)
));
} else {
completer.completeError(message);
} }
});
Isolate.spawn( final semaphore = Semaphore(1);
_startProcess, void logEvent(String event) async {
_ProcessParameters(executable, args, window, port.sendPort), await semaphore.acquire();
errorsAreFatal: true await loggingFile.writeAsString("$event\n", mode: FileMode.append, flush: true);
); semaphore.release();
return await completer.future; }
extendedProcess.stdOutput.listen(logEvent);
extendedProcess.stdError.listen(logEvent);
if(!window) {
extendedProcess.exitCode.then((value) => logEvent("Process terminated with exit code: $value\n"));
}
return extendedProcess;
} }
final _NtResumeProcess = _ntdll.lookupFunction<Int32 Function(IntPtr hWnd), final _NtResumeProcess = _ntdll.lookupFunction<Int32 Function(IntPtr hWnd),
@@ -247,8 +161,11 @@ bool resume(int pid) {
void _watchProcess(int pid) { void _watchProcess(int pid) {
final processHandle = OpenProcess(SYNCHRONIZE, FALSE, pid); final processHandle = OpenProcess(SYNCHRONIZE, FALSE, pid);
try {
WaitForSingleObject(processHandle, INFINITE); WaitForSingleObject(processHandle, INFINITE);
}finally {
CloseHandle(processHandle); CloseHandle(processHandle);
}
} }
Future<bool> watchProcess(int pid) async { Future<bool> watchProcess(int pid) async {
@@ -261,16 +178,14 @@ Future<bool> watchProcess(int pid) async {
}); });
var errorPort = ReceivePort(); var errorPort = ReceivePort();
errorPort.listen((_) => completer.complete(false)); errorPort.listen((_) => completer.complete(false));
var isolate = await Isolate.spawn( await Isolate.spawn(
_watchProcess, _watchProcess,
pid, pid,
onExit: exitPort.sendPort, onExit: exitPort.sendPort,
onError: errorPort.sendPort, onError: errorPort.sendPort,
errorsAreFatal: true errorsAreFatal: true
); );
var result = await completer.future; return await completer.future;
isolate.kill(priority: Isolate.immediate);
return result;
} }
List<String> createRebootArgs(String username, String password, bool host, bool headless, String additionalArgs) { List<String> createRebootArgs(String username, String password, bool host, bool headless, String additionalArgs) {
@@ -325,3 +240,52 @@ String _parseUsername(String username, bool host) {
return username; return username;
} }
class _ExtendedProcess extends Process {
final Process _delegate;
final Stream<List<int>>? _stdout;
final Stream<List<int>>? _stderr;
_ExtendedProcess(Process delegate, bool attached) :
_delegate = delegate,
_stdout = attached ? delegate.stdout.asBroadcastStream() : null,
_stderr = attached ? delegate.stderr.asBroadcastStream() : null;
@override
Future<int> get exitCode => _delegate.exitCode;
@override
bool kill([ProcessSignal signal = ProcessSignal.sigterm]) => _delegate.kill(signal);
@override
int get pid => _delegate.pid;
@override
IOSink get stdin => _delegate.stdin;
@override
Stream<List<int>> get stdout {
final out = _stdout;
if(out == null) {
throw StateError("Output is not attached");
}
return out;
}
@override
Stream<List<int>> get stderr {
final err = _stderr;
if(err == null) {
throw StateError("Output is not attached");
}
return err;
}
}
extension ProcessExtension on Process {
Stream<String> get stdOutput => this.stdout.expand((event) => utf8.decode(event).split("\n"));
Stream<String> get stdError => this.stderr.expand((event) => utf8.decode(event).split("\n"));
}

View File

@@ -17,6 +17,8 @@ dependencies:
ini: ^2.1.0 ini: ^2.1.0
shelf_proxy: ^1.0.2 shelf_proxy: ^1.0.2
sync: ^0.3.0 sync: ^0.3.0
uuid: ^3.0.6
shelf_web_socket: ^2.0.0
dev_dependencies: dev_dependencies:
flutter_lints: ^2.0.1 flutter_lints: ^2.0.1

View File

@@ -1,29 +0,0 @@
import 'dart:io';
import 'package:reboot_common/common.dart';
import 'package:supabase/supabase.dart';
void main(List<String> args) async {
if(args.length != 5){
stderr.writeln("Wrong args length: $args");
return;
}
var supabase = SupabaseClient(supabaseUrl, supabaseAnonKey);
var uuid = args[0];
var gamePid = int.parse(args[1]);
var launcherPid = int.parse(args[2]);
var eacPid = int.parse(args[3]);
var hosting = args[4].toLowerCase() == "true";
await watchProcess(gamePid);
Process.killPid(gamePid, ProcessSignal.sigabrt);
Process.killPid(launcherPid, ProcessSignal.sigabrt);
Process.killPid(eacPid, ProcessSignal.sigabrt);
if(hosting) {
await supabase.from("hosting")
.delete()
.match({'id': uuid});
}
exit(0);
}

View File

@@ -1,15 +0,0 @@
name: reboot_watch
version: "1.0.0"
publish_to: 'none'
environment:
sdk: ">=2.19.0 <=3.3.4"
dependencies:
supabase: ^1.9.1
reboot_common:
path: ./../common
dev_dependencies:
flutter_lints: ^2.0.1

View File

@@ -1,13 +0,0 @@
# ما هو مشروع Reboot؟
[Project Reboot](https://github.com/Milxnor/Project-Reboot-3.0) هو خادم ألعاب لـ Fortnite S3-S15
### من الذي بدأ المشروع؟
بدأ المشروع على Discord بواسطة Milxnor
[انضم إلى الديسكورد!](https://discord.gg/reboot)
### من قام بتطوير الانشر؟
تم تطوير المشغل بالكامل بواسطة [Auties00](https://github.com/Auties00/reboot_launcher)
شكرًا لكل من ساهم في ترجمة المشغل إلى لغات أخرى!
### هل تتم صيانة المشروع بشكل نشط؟
لم تعد تتم صيانة المشروع بشكل نشط بعد الآن، ولكن لا يزال بإمكان المجتمع المساهمة في تطويره.

View File

@@ -1,19 +0,0 @@
# أين يمكنني الإبلاغ عن الأخطاء؟
## خادم اللعبة
### 1. جيثب
افتح مشكلة على [Github](https://github.com/Milxnor/Project-Reboot-3.0/issues)
### 2. الديسكورد
اسأل في [#help](https://discord.gg/reboot)
## الانشر
### 1. جيثب
افتح مشكلة على [Github](https://github.com/Milxnor/Project-Reboot-3.0/issues)
### 2. الديسكورد
ضع علامة علىAuties في [#launcher-issues](https://discord.gg/reboot)

View File

@@ -1,37 +0,0 @@
# كيف يمكنني جعل الخادم الخاص بي في مفتوح اللاعبين الآخرين؟
### 1. قم بتعيين عنوان IP ثابت
قم بتعيين عنوان IP ثابت على جهاز الكمبيوتر الذي يستضيف خادم اللعبة وانسخه لوقت لاحق:
- [ويندوز 11](https://pureinfotech.com/set-static-ip-address-windows-11/)
- [ويندوز 10](https://pureinfotech.com/set-static-ip-address-windows-10/)
### 2. قم بتسجيل الدخول إلى جهاز التوجيه الخاص بك
ستحتاج إلى الوصول إلى واجهة الويب الخاصة بجهاز التوجيه الخاص بك على 192.168.1.1.
قد تحتاج إلى اسم مستخدم وكلمة مرور لتسجيل الدخول: راجع دليل جهاز التوجيه الخاص بك للحصول على تعليمات دقيقة.
### 3. ابحث عن قسم إعادة توجيه المنفذ
بمجرد تسجيل الدخول، انتقل إلى قسم إعادة توجيه المنفذ في إعدادات جهاز التوجيه الخاص بك.
قد يختلف هذا الموقع من جهاز توجيه إلى آخر، ولكن يُطلق عليه عادةً اسم "Port Forwarding" أو "Port Mapping" أو "Virtual Server".
ارجع إلى دليل جهاز التوجيه الخاص بك للحصول على تعليمات دقيقة.
### 4. أضف قاعدة إعادة توجيه المنفذ
الآن، ستحتاج إلى إنشاء قاعدة جديدة لإعادة توجيه المنفذ. إليك ما ستحتاج عادةً إلى تحديده:
- **اسم الخدمة:** اختر اسمًا لقاعدة إعادة توجيه المنفذ (على سبيل المثال، "Fortnite Game Server").
- **رقم المنفذ:** أدخل 7777 لكل من المنافذ الخارجية والداخلية.
- **البروتوكول:** حدد بروتوكول UDP.
- **عنوان IP الداخلي:** أدخل عنوان IP الثابت الذي قمت بتعيينه مسبقًا.
- **تمكين:** تأكد من تمكين قاعدة إعادة توجيه المنفذ.
### 5. احفظ التغييرات وقم بتطبيقها
بعد تكوين قاعدة إعادة توجيه المنفذ، احفظ التغييرات وقم بتطبيقها.
قد تتضمن هذه الخطوة النقر فوق الزر "حفظ" أو "تطبيق" الموجود على واجهة الويب الخاصة بجهاز التوجيه الخاص بك.
### 6. حاول استضافة لعبة!

View File

@@ -1,13 +0,0 @@
# Was ist Projekt Reboot?
[Projekt Reboot](https://github.com/Milxnor/Project-Reboot-3.0) is a game server for Fortnite S3-S15
### Wer hat das Projekt gestartet?
Das Projekt wurde auf Discord von Milxnor gestartet
[Trete dem Discord Server bei!](https://discord.gg/reboot)
### Wer entwickelt den Launcher?
Der Launcher is komplett von [Auties00](https://github.com/Auties00/reboot_launcher) entwickelt.
Danke an alle, die den Launcher in andere Sprachen übersetzt haben!
### Wird an dem Projekt noch aktiv gearbeitet?
An dem Projekt wird nicht mehr aktiv gearbeitet, aber die Community kann immer noch zu seiner Entwicklung beitragen.

View File

@@ -1,22 +0,0 @@
# Wo kann ich Bugs/Fehler einsenden?
## Game-Server
### 1. Github
Öffne ein Problem auf [Github](https://github.com/Milxnor/Project-Reboot-3.0/issues)
### 2. Discord
Frag in [#help](https://discord.gg/reboot)
## Launcher
### 1. Github
Öffne ein Problem auf [Github](https://github.com/Milxnor/Project-Reboot-3.0/issues)
### 2. Discord
Markiere @Auties in [#launcher-issues](https://discord.gg/reboot)

View File

@@ -1,37 +0,0 @@
# Wie kann ich andere Spieler auf meinen Server lassen?
### 1. Setze eine statische IP-Adresse (Static IP)
Setze eine statische IP-Adresse auf dem PC, auf dem der Server läuft und kopiere sie für später:
- [Windows 11](https://pureinfotech.com/set-static-ip-address-windows-11/)
- [Windows 10](https://pureinfotech.com/set-static-ip-address-windows-10/)
### 2. Logge dich in deinen Router ein
Du wirst das Web-Interface von deinem Router öffnen bei 192.168.1.1 müssen.
Vielleicht wirst du einen Benutzernamen und ein Passwort für den Log-In benötigen: Schau auf der Bedienung für deinen Router nach für prezise Vorgehensweisen.
### 3. Finde die Port-Forwarding Abteilung
Nachdem Sie sich angemeldet haben, gehen Sie zum Portweiterleitungsbereich der Einstellungen Ihres Routers.
Dieser Ort kann von Router zu Router variieren, ist jedoch normalerweise als "Portweiterleitung," "Portzuordnung" oder "Virtueller Server" gekennzeichnet.
Konsultieren Sie das Handbuch Ihres Routers für genaue Anweisungen.
### 4. Eine Port-Forwarding Regel hinzufügen
Nun müssen Sie eine neue Portweiterleitungsregel erstellen. Hier ist, was Sie normalerweise angeben müssen:
- **Dienstname**: Wählen Sie einen Namen für Ihre Portweiterleitungsregel (z.B. "Fortnite Game Server").
- **Portnummer**: Geben Sie sowohl für die externen als auch für die internen Ports die Nummer 7777 ein.
- **Protokoll**: Wählen Sie das UDP-Protokoll.
- **Interne IP-Adresse**: Geben Sie die statische IP-Adresse ein, die Sie zuvor festgelegt haben.
- **Aktivieren**: Stellen Sie sicher, dass die Portweiterleitungsregel aktiviert ist.
### 5. Änderungen speichern
Nachdem Sie die Portweiterleitungsregel konfiguriert haben, speichern Sie Ihre Änderungen und wenden Sie sie an.
Dieser Schritt kann das Klicken auf eine "Speichern" oder "Anwenden" Schaltfläche auf der Weboberfläche Ihres Routers erfordern.
### 6. Versuch, ein Spiel zu hosten!

View File

@@ -1,13 +0,0 @@
# What is Project Reboot?
[Project Reboot](https://github.com/Milxnor/Project-Reboot-3.0) is a game server for Fortnite S3-S15
### Who started the project?
The project was started on Discord by Milxnor
[Join the discord!](https://discord.gg/reboot)
### Who develops the launcher?
The launcher is entirely developed by [Auties00](https://github.com/Auties00/reboot_launcher)
Thanks to everyone who contributed to translate the launcher in other languages!
### Is the project being actively maintained?
The project isn't actively maintained anymore, but the community can still contribute to its development.

View File

@@ -1,20 +0,0 @@
# Where can I report bugs?
## Game server
### 1. Github
Open an issue on [Github](https://github.com/Milxnor/Project-Reboot-3.0/issues)
### 2. Discord
Ask in [#help](https://discord.gg/reboot)
## Launcher
### 1. Github
Open an issue on [Github](https://github.com/Milxnor/Project-Reboot-3.0/issues)
### 2. Discord
Tag @Auties in [#launcher-issues](https://discord.gg/reboot)

View File

@@ -1,37 +0,0 @@
# How can I make my server accessible to other players?
### 1. Set a static IP
Set a static IP on the PC hosting the game server and copy it for later:
- [Windows 11](https://pureinfotech.com/set-static-ip-address-windows-11/)
- [Windows 10](https://pureinfotech.com/set-static-ip-address-windows-10/)
### 2. Log into Your Router
You'll need to access your router's web interface at 192.168.1.1.
You might need a username and a password to log in: refer to your router's manual for precise instructions.
### 3. Find the Port Forwarding Section
Once logged in, navigate to the port forwarding section of your router's settings.
This location may vary from router to router, but it's typically labelled as "Port Forwarding," "Port Mapping," or "Virtual Server."
Refer to your router's manual for precise instructions.
### 4. Add a Port Forwarding Rule
Now, you'll need to create a new port forwarding rule. Here's what you'll typically need to specify:
- **Service Name:** Choose a name for your port forwarding rule (e.g., "Fortnite Game Server").
- **Port Number:** Enter 7777 for both the external and internal ports.
- **Protocol:** Select the UDP protocol.
- **Internal IP Address:** Enter the static IP address you set earlier.
- **Enable:** Make sure the port forwarding rule is enabled.
### 5. Save and Apply the Changes
After configuring the port forwarding rule, save your changes and apply them.
This step may involve clicking a "Save" or "Apply" button on your router's web interface.
### 6. Try hosting a game!

View File

@@ -1,12 +0,0 @@
# ¿Qué es Project Reboot?
[Project Reboot](https://github.com/Milxnor/Project-Reboot-3.0) es un servidor de juego para Fortnite que abarca desde la temporada 3 hasta la temporada 15.
### ¿Quién inició el proyecto?
El proyecto fue iniciado en Discord por Milxnor.
[¡Únete al Discord!](https://discord.gg/reboot)
### ¿Quién desarrolla el lanzador?
El lanzador está completamente desarrollado por [Auties00](https://github.com/Auties00/reboot_launcher). ¡Agradecemos a todos los que contribuyeron a traducir el lanzador a otros idiomas!
### ¿El proyecto se mantiene activamente?
El proyecto ya no se encuentra bajo mantenimiento activo, pero la comunidad aún puede contribuir a su desarrollo.

View File

@@ -1,20 +0,0 @@
# ¿Dónde puedo reportar errores?
## Servidor de Juego
### 1. Github
Abre un issue en [Github](https://github.com/Milxnor/Project-Reboot-3.0/issues)
### 2. Discord
Pregunta en [#help](https://discord.gg/reboot)
## Launcher
### 1. Github
Abre un issue en [Github](https://github.com/Milxnor/Project-Reboot-3.0/issues)
### 2. Discord
Menciona a @Auties en [#launcher-issues](https://discord.gg/reboot)

View File

@@ -1,36 +0,0 @@
# ¿Cómo puedo hacer que mi servidor sea accesible para otros jugadores?
### 1. Configurar una IP estática
Establece una IP estática en la PC que aloja el servidor de juego y cópiala para usarla más tarde:
- [Windows 11](https://pureinfotech.com/set-static-ip-address-windows-11/)
- [Windows 10](https://pureinfotech.com/set-static-ip-address-windows-10/)
### 2. Iniciar sesión en tu Router
Necesitarás acceder a la interfaz web de tu router en 192.168.1.1.
Es posible que necesites un nombre de usuario y una contraseña para iniciar sesión: consulta el manual de tu router para instrucciones precisas.
### 3. Encuentra la Sección de Reenvío de Puertos
Una vez iniciada la sesión, ve a la sección de reenvío de puertos en la configuración de tu router.
Esta ubicación puede variar de un router a otro, pero generalmente está etiquetada como "Reenvío de Puertos", "Mapeo de Puertos" o "Servidor Virtual".
Consulta el manual de tu router para instrucciones precisas.
### 4. Agrega una Regla de Reenvío de Puertos
Ahora, deberás crear una nueva regla de reenvío de puertos. Esto es lo que normalmente necesitarás especificar:
- **Nombre del Servicio:** Elige un nombre para tu regla de reenvío de puertos (por ejemplo, "Servidor de Juego Fortnite").
- **Número de Puerto:** Ingresa 7777 tanto para los puertos externos como internos.
- **Protocolo:** Selecciona el protocolo UDP.
- **Dirección IP Interna:** Ingresa la dirección IP estática que configuraste anteriormente.
- **Habilitar:** Asegúrate de que la regla de reenvío de puertos esté habilitada.
### 5. Guarda y Aplica los Cambios
Después de configurar la regla de reenvío de puertos, guarda tus cambios y aplícalos.
Este paso puede implicar hacer clic en un botón "Guardar" o "Aplicar" en la interfaz web de tu router.
### 6. ¡Intenta hospedar un servidor de juego!

View File

@@ -1,13 +0,0 @@
# Qu'est ce que c'est le "Project Reboot" ?
["Project Reboot"](https://github.com/Milxnor/Project-Reboot-3.0) est un serveur de jeu pour Fortnite S3-S15
### Qui a lancé le projet ?
Le projet à été lancé sur Discord par Milxnor
[Rejoignez le Discord !](https://discord.gg/reboot)
### Qui développe le launcher de Reboot?
Le launcher est entièrement développé par [Auties00](https://github.com/Auties00/reboot_launcher)
Merci à tous ceux qui contribue à la traduction du launcher dans d'autres languages ! (🇫🇷 Dj_Mc01 & Dixip)
### Le projet est-il toujours mis à jour ?
Le projet n'est malheuresement plus activement maintenu, mais la communauté peut toujours contribuer au développement du [projet](https://github.com/Milxnor/Project-Reboot-3.0).

View File

@@ -1,20 +0,0 @@
# Comment puis-je reporter des Bugs?
## Serveur de jeu
### 1. Github
Ouvrez une "issue" sur le [Github](https://github.com/Milxnor/Project-Reboot-3.0/issues)
### 2. Discord
Demandez dans [#help](https://discord.gg/reboot)
## Launcher
### 1. Github
Ouvrez une "issue" sur le [Github](https://github.com/Milxnor/Project-Reboot-3.0/issues)
### 2. Discord
Taggez @Auties dans [#launcher-issues](https://discord.gg/reboot)

View File

@@ -1,41 +0,0 @@
# Comment faire pour que mon serveur puisse être accessible par les autres joueurs ?
### 1. Définir une adresse IP locale statique (optionnel)
Définissez une adresse statique puis copiez là pour plus tard:
- [Windows 11](https://pureinfotech.com/set-static-ip-address-windows-11/)
- [Windows 10](https://pureinfotech.com/set-static-ip-address-windows-10/)
### 2. Se connecter sur l'interface de configuration de votre routeur
Pour y accéder, rendez-vous sur la bonne adresse, cela dépend de votre routeur (Orange & SFR: 192.168.1.1, Free: mafreebox.freebox.fr, Bouygues: 192.168.1.254)
**!!!Attention!!! certains opérateurs (comme SFR & Free) ne laissent plus accès à une IPv4 propre à leurs clients**, dans le cas de Free, vous devrez commander une ["IP FullStack"](https://subscribe.free.fr/login/) (c'est gratuit). Dans le cas d'SFR, vous devrez batailler avec le support client (ne perdez pas espoir, j'y suis parvenu en une semaine): +33 1023
Vous trouverez les informations d'authentifications dans le manuel de votre Box.
### 3. Trouver la section de configuration des ports
Une fois connectez sur le panel et votre IPv4 obtenue, cherchez la page des options pour ouvrir vos ports
Ceci varie beacoup en fonction du routeur, mais c'est souvent appelée "NAT/PAT", "Gestion des ports" ou encore "Port Forwarding".
Sinon, réferez vous à des guides sur Google ou autre...
### 4. Ajouter la règle
Maintenant, vous devez créer la règle pour autoriser les joueurs à se connecter, rentrez les informations suivantes:
- **Nom:** Choisissez un nom pour votre règle, "Fortnite serveur" ? Peu importe...
- **Ports:** Entrez 7777 pour le port interne et externe (s'il vous demande une plage, spécifiez 7777 partout).
- **Protocole:** Sélectionnez UDP (ou les deux si possible).
- **IP de destination:** Entrez l'IP locale de votre serveur, celle précédemment spécifiée, ou sélectionnez votre appareil par son nom dans la liste (si disponible).
- **Activation:** Soyez-sûre que la règle est activée, bien évidemment.
### 5. Sauvegardez et appliquez les changements.
Après avoir spécifié votre règle, n'oubliez pas de sauvegarder.
### 6. Essayez de lancer une partie !
Si tout s'est correctement déroulé, les joueurs devraient être en possibilité de rejoindre votre serveur.

View File

@@ -1,13 +0,0 @@
# Czym jest Project Reboot?
[Project Reboot](https://github.com/Milxnor/Project-Reboot-3.0) to serwer gry dla Fortnite S3-S15
### Kto rozpoczął projekt?
Projekt został zapoczątkowany na Discordzie przez Milxnora
[Dołącz do Discorda!](https://discord.gg/reboot)
### Kto rozwija launcher?
Program uruchamiający został w całości opracowany przez [Auties00](https://github.com/Auties00/reboot_launcher).
Dziękujemy wszystkim, którzy przyczynili się do przetłumaczenia launchera na inne języki! (Przetłumaczono na Polski przez: TKD_Kedis)
### Czy projekt jest aktywnie rozwijany?
Projekt nie jest już aktywnie rozwijany, ale społeczność nadal może przyczyniać się do jego rozwoju.

View File

@@ -1,20 +0,0 @@
# Gdzie mogę zgłaszać błędy?
## Serwer gry
### 1. Github
Otwórz zgłoszenie na [Github](https://github.com/Milxnor/Project-Reboot-3.0/issues)
### 2. Discord
Zapytaj w [#help](https://discord.gg/reboot)
## Program uruchamiający
### 1. Github
Otwórz zgłoszenie na [Github](https://github.com/Milxnor/Project-Reboot-3.0/issues)
### 2. Discord
Oznacz @Auties w [#launcher-issues](https://discord.gg/reboot)

View File

@@ -1,37 +0,0 @@
# Jak mogę udostępnić mój serwer innym graczom?
### 1. Ustaw statyczny adres IP
Ustaw statyczny adres IP na komputerze hostującym serwer gry i skopiuj go na później:
- [Windows 11](https://pureinfotech.com/set-static-ip-address-windows-11/)
- [Windows 10](https://pureinfotech.com/set-static-ip-address-windows-10/)
### 2. Zaloguj się do routera
Musisz uzyskać dostęp do interfejsu internetowego routera pod adresem 192.168.1.1.
Do zalogowania może być potrzebna nazwa użytkownika i hasło: dokładne instrukcje można znaleźć w instrukcji obsługi routera.
### 3. Znajdź sekcję Przekierowanie portów
Po zalogowaniu przejdź do sekcji przekierowania portów w ustawieniach routera.
Lokalizacja ta może się różnić w zależności od routera, ale zazwyczaj jest oznaczona jako "Przekierowanie portów", "Mapowanie portów" lub "Serwer wirtualny".
Dokładne instrukcje można znaleźć w instrukcji obsługi routera.
### 4. Dodaj regułę przekierowania portów
Teraz musisz utworzyć nową regułę przekierowania portów. Oto, co zazwyczaj należy określić:
- **Nazwa usługi:** Wybierz nazwę dla swojej reguły przekierowania portów (np. "Fortnite Game Server").
- **Numer portu:** Wprowadź 7777 dla portów zewnętrznych i wewnętrznych.
- **Protokół:** Wybierz protokół UDP.
- **Wewnętrzny adres IP:** Wprowadź statyczny adres IP, który ustawiłeś wcześniej.
- **Włącz:** Upewnij się, że reguła przekierowania portów jest włączona.
### 5. Zapisz i zastosuj zmiany
Po skonfigurowaniu reguły przekierowania portów, zapisz zmiany i zastosuj je.
Ten krok może wymagać kliknięcia przycisku "Zapisz" lub "Zastosuj" na stronie internetowej routera.
### 6. Spróbuj zhostować grę!

View File

@@ -1,12 +0,0 @@
# O que é o Projeto Reboot?
[Projeto Reboot](https://github.com/Milxnor/Project-Reboot-3.0) é um servidor de jogo para Fortnite da temporada 3 à temporada 15.
### Quem iniciou o projeto?
O projeto foi iniciado no Discord por Milxnor.
[Entre no Discord!](https://discord.gg/reboot)
### Quem desenvolve o launcher?
O launcher é completamente desenvolvido por [Auties00](https://github.com/Auties00/reboot_launcher). Agradecemos a todos que contribuíram para traduzir o launcher para outros idiomas!
### O projeto está sendo ativamente mantido?
O projeto não está mais sendo ativamente mantido, mas a comunidade ainda pode contribuir para o seu desenvolvimento.

View File

@@ -1,20 +0,0 @@
# Onde posso reportar bugs?
## Servidor de Jogo
### 1. Github
Abra um issue no [Github](https://github.com/Milxnor/Project-Reboot-3.0/issues)
### 2. Discord
Pergunte em [#help](https://discord.gg/reboot)
## Launcher
### 1. Github
Abra um issue no [Github](https://github.com/Milxnor/Project-Reboot-3.0/issues)
### 2. Discord
Marque @Auties em [#launcher-issues](https://discord.gg/reboot)

View File

@@ -1,37 +0,0 @@
# Como posso tornar meu servidor acessível para outros jogadores?
### 1. Configure um IP estático
Configure um IP estático no PC que hospeda o servidor de jogo e copie-o para uso posterior:
- [Windows 11](https://pureinfotech.com/set-static-ip-address-windows-11/)
- [Windows 10](https://pureinfotech.com/set-static-ip-address-windows-10/)
### 2. Faça login no seu Roteador
Você precisará acessar a interface web do seu roteador em 192.168.1.1 ou 192.168.0.1.
Lembre-se que a IP de acesso ao seu roteador pode mudar conforme o seu provedor de internet.
Poderá ser necessário um nome de usuário e senha para fazer o login: consulte o manual do seu roteador para instruções precisas.
### 3. Encontre a Seção de Redirecionamento de Porta
Após fazer login, vá para a seção de redirecionamento de porta nas configurações do seu roteador.
A localização pode variar de um roteador para outro, mas geralmente é rotulada como "Redirecionamento de Porta," "Mapeamento de Porta" ou "Servidor Virtual."
Consulte o manual do seu roteador para instruções precisas.
### 4. Adicione uma Regra de Redirecionamento de Porta
Agora, você precisará criar uma nova regra de redirecionamento de porta. Eis o que normalmente você precisará especificar:
- **Nome do Serviço:** Escolha um nome para sua regra de redirecionamento de porta (por exemplo, "Servidor de Jogo Fortnite").
- **Número da Porta:** Insira 7777 para ambas as portas externa e interna.
- **Protocolo:** Selecione o protocolo UDP.
- **Endereço IP Interno:** Insira o endereço IP estático que você configurou anteriormente.
- **Ativar:** Certifique-se de que a regra de redirecionamento de porta esteja ativada.
### 5. Salve e Aplique as Alterações
Após configurar a regra de redirecionamento de porta, salve suas alterações e aplique-as.
Isso pode envolver clicar em um botão "Salvar" ou "Aplicar" na interface web do seu roteador.
### 6. Tente hospedar um jogo!

View File

@@ -1,13 +0,0 @@
# Ce este Proiectul Reboot?
[Proiectul Reboot](https://github.com/Milxnor/Project-Reboot-3.0) este un server de joc pentru Fortnite S3-S15
### Cine a inițiat proiectul?
Proiectul a fost inițiat pe Discord de Milxnor
[Intrați pe discord!](https://discord.gg/reboot)
### Cine dezvoltă lansatorul?
Lansatorul este dezvoltat în întregime de [Auties00](https://github.com/Auties00/reboot_launcher)
Mulțumim tuturor celor care au contribuit la traducerea lansatorului în alte limbi!
### Proiectul este întreținut în mod activ?
Proiectul nu mai este întreținut în mod activ, dar comunitatea poate contribui în continuare la dezvoltarea sa.

View File

@@ -1,20 +0,0 @@
# Unde pot raporta bug-uri?
## Server de joc
### 1. Github
Deschideți o problemă pe [Github](https://github.com/Milxnor/Project-Reboot-3.0/issues)
### 2. Discord
Întrebați pe canalul [#help](https://discord.gg/reboot)
## Lansator
### 1. Github
Deschideți o problemă pe [Github](https://github.com/Milxnor/Project-Reboot-3.0/issues)
### 2. Discord
Etichetați @Auties în [#launcher-issues](https://discord.gg/reboot)

View File

@@ -1,37 +0,0 @@
# Cum îmi pot face serverul accesibil altor jucători?
### 1. Setați un IP static
Setați un IP static pe PC-ul care găzduiește serverul de joc și copiați-l pentru mai târziu:
- [Windows 11](https://pureinfotech.com/set-static-ip-address-windows-11/)
- [Windows 10](https://pureinfotech.com/set-static-ip-address-windows-10/)
### 2. Conectați-vă la router
Va trebui să accesați interfața web a routerului la 192.168.1.1.
Este posibil să aveți nevoie de un nume de utilizator și de o parolă pentru a vă conecta: consultați manualul routerului pentru instrucțiuni precise.
### 3. Găsiți secțiunea Port Forwarding
Odată ce v-ați conectat, navigați la secțiunea de Port Forwarding din setările routerului.
Această secțiune poate varia de la un router la altul, dar de obicei este etichetată ca "Port Forwarding", "Port Mapping" sau "Virtual Server".
Consultați manualul routerului dumneavoastră pentru instrucțiuni precise.
### 4. Adăugați o regulă pentru Port Forwarding
Acum va trebui să creați o nouă regulă pentru "Port Forwarding". Iată ce va trebui să specificați în mod obișnuit:
- **Denumirea serviciului:** Alegeți un nume pentru Port Forwarding (de exemplu, "Fortnite Game Server").
- **Numărul portului:** Introduceți 7777 atât pentru portul extern, cât și pentru cel intern.
- **Protocolul:** Selectați protocolul UDP.
- **Adresa IP internă:** Introduceți adresa IP statică pe care ați introdus-o mai devreme.
- **Activați:** Asigurați-vă că regula de redirecționare a portului este activată.
### 5. Salvați și Aplicați modificările
După configurarea Port Forwarding-ului, salvați modificările și aplicați-le.
Acest pas poate implica apăsarea butonului "Save" (Salvare) sau "Apply" (Aplicare) pe interfața web a routerului.
### 6. Încercați să găzduiți un meci!

View File

@@ -1,13 +0,0 @@
# Что такое проект Reboot?
[Project Reboot](https://github.com/Milxnor/Project-Reboot-3.0) - это игровой сервер для Fortnite с сезона 3 по 15.
### Кто начал этот проект?
Проект был начат на сервере Discord пользователем с никнеймом Milxnor.
[Присоединяйтесь к серверу Discord!](https://discord.gg/reboot)
### Кто разрабатывает лаунчер?
Лаунчер полностью разрабатывается [Auties00](https://github.com/Auties00/reboot_launcher).
Спасибо всем, кто внес свой вклад в перевод лаунчера на другие языки!
### Проект активно поддерживается?
Проект больше не активно поддерживается, однако сообщество все еще может вносить свой вклад в его развитие.

View File

@@ -1,19 +0,0 @@
# Где я могу сообщить о багах?
## Сервер
### 1. Github
Создайте запрос на [Github](https://github.com/Milxnor/Project-Reboot-3.0/issues)
### 2. Discord
Задайте вопрос в [#help](https://discord.gg/reboot)
## Лаунчер
### 1. Github
Создайте запрос на [Github](https://github.com/Milxnor/Project-Reboot-3.0/issues)
### 2. Discord
Отметьте пользователя @Auties в [#launcher-issues](https://discord.gg/reboot)

View File

@@ -1,35 +0,0 @@
# Как сделать свой сервер доступным для других игроков?
### 1. Задайте статический IP
Установите статический IP на ПК, на котором размещен игровой сервер, и скопируйте его для последующего использования:
- [Windows 11](https://pureinfotech.com/set-static-ip-address-windows-11/)
- [Windows 10](https://pureinfotech.com/set-static-ip-address-windows-10/)
### 2. Войдите в свой роутер
Для доступа к веб-интерфейсу вашего роутера перейдите по адресу 192.168.1.1.
Может потребоваться имя пользователя и пароль для входа: обратитесь к руководству вашего роутера за точными инструкциями.
### 3. Найдите раздел "Перенаправление портов"
После входа перейдите в раздел настроек роутера, связанный с перенаправлением портов. Местоположение этого раздела может различаться от роутера к роутеру, но обычно он обозначается как "Перенаправление портов", "Назначение портов" или "Виртуальный сервер".
Смотрите в руководстве к роутеру для точных инструкций.
### 4. Добавьте правило перенаправления портов
Теперь вам нужно создать новое правило перенаправления портов. Вот что обычно нужно указать:
- **Имя сервиса:** Выберите имя для вашего правила перенаправления портов (например, "Игровой сервер Fortnite").
- **Номер порта:** Введите 7777 как для внешнего, так и для внутреннего порта.
- **Протокол:** Выберите протокол UDP.
- **Внутренний IP-адрес:** Введите статический IP-адрес, который вы установили ранее.
- **Включить:** Убедитесь, что правило перенаправления портов включено.
### 5. Сохраните и примените изменения
После настройки правила перенаправления портов сохраните ваши изменения и примените их.
Этот шаг может включать в себя нажатие кнопки "Сохранить" или "Применить" на веб-интерфейсе вашего роутера.
### 6. Попробуйте запустить сервер!

View File

@@ -1,11 +0,0 @@
# Do not remove/change, this redirects epicgames xmpp to lawinserver xmpp
[OnlineSubsystemMcp.Xmpp]
bUseSSL=false
ServerAddr="ws://127.0.0.1"
ServerPort=80
# Do not remove/change, this redirects epicgames xmpp to lawinserver xmpp
[OnlineSubsystemMcp.Xmpp Prod]
bUseSSL=false
ServerAddr="ws://127.0.0.1"
ServerPort=80

View File

@@ -1,526 +0,0 @@
[
"Defender:did_defenderassault_basic_c_t01",
"Defender:did_defenderassault_basic_r_t01",
"Defender:did_defenderassault_basic_sr_t01",
"Defender:did_defenderassault_basic_uc_t01",
"Defender:did_defenderassault_basic_vr_t01",
"Defender:did_defenderassault_founders_vr_t01",
"Defender:did_defendermelee_basic_c_t01",
"Defender:did_defendermelee_basic_r_t01",
"Defender:did_defendermelee_basic_sr_t01",
"Defender:did_defendermelee_basic_uc_t01",
"Defender:did_defendermelee_basic_vr_t01",
"Defender:did_defenderpistol_basic_c_t01",
"Defender:did_defenderpistol_basic_r_t01",
"Defender:did_defenderpistol_basic_sr_t01",
"Defender:did_defenderpistol_basic_uc_t01",
"Defender:did_defenderpistol_basic_vr_t01",
"Defender:did_defenderpistol_founders_vr_t01",
"Defender:did_defendershotgun_basic_c_t01",
"Defender:did_defendershotgun_basic_r_t01",
"Defender:did_defendershotgun_basic_sr_t01",
"Defender:did_defendershotgun_basic_uc_t01",
"Defender:did_defendershotgun_basic_vr_t01",
"Defender:did_defendersniper_basic_c_t01",
"Defender:did_defendersniper_basic_r_t01",
"Defender:did_defendersniper_basic_sr_t01",
"Defender:did_defendersniper_basic_uc_t01",
"Defender:did_defendersniper_basic_vr_t01",
"Hero:hid_commando_007_r_t01",
"Hero:hid_commando_007_sr_t01",
"Hero:hid_commando_007_uc_t01",
"Hero:hid_commando_007_vr_t01",
"Hero:hid_commando_008_foundersf_sr_t01",
"Hero:hid_commando_008_foundersm_sr_t01",
"Hero:hid_commando_008_r_t01",
"Hero:hid_commando_008_sr_t01",
"Hero:hid_commando_008_vr_t01",
"Hero:hid_commando_009_r_t01",
"Hero:hid_commando_009_sr_t01",
"Hero:hid_commando_009_vr_t01",
"Hero:hid_commando_010_sr_t01",
"Hero:hid_commando_010_vr_t01",
"Hero:hid_commando_gcgrenade_r_t01",
"Hero:hid_commando_gcgrenade_sr_t01",
"Hero:hid_commando_gcgrenade_vr_t01",
"Hero:hid_commando_grenadegun_r_t01",
"Hero:hid_commando_grenadegun_sr_t01",
"Hero:hid_commando_grenadegun_uc_t01",
"Hero:hid_commando_grenadegun_vr_t01",
"Hero:hid_commando_grenademaster_sr_t01",
"Hero:hid_commando_gunheadshot_sr_t01",
"Hero:hid_commando_gunheadshot_vr_t01",
"Hero:hid_commando_gunheadshothw_sr_t01",
"Hero:hid_commando_guntough_r_t01",
"Hero:hid_commando_guntough_sr_t01",
"Hero:hid_commando_guntough_uc_t01",
"Hero:hid_commando_guntough_vr_t01",
"Hero:hid_commando_shockdamage_r_t01",
"Hero:hid_commando_shockdamage_sr_t01",
"Hero:hid_commando_shockdamage_vr_t01",
"Hero:hid_commando_sony_r_t01",
"Hero:hid_constructor_007_r_t01",
"Hero:hid_constructor_007_sr_t01",
"Hero:hid_constructor_007_uc_t01",
"Hero:hid_constructor_007_vr_t01",
"Hero:hid_constructor_008_foundersf_sr_t01",
"Hero:hid_constructor_008_foundersm_sr_t01",
"Hero:hid_constructor_008_r_t01",
"Hero:hid_constructor_008_sr_t01",
"Hero:hid_constructor_008_vr_t01",
"Hero:hid_constructor_009_r_t01",
"Hero:hid_constructor_009_sr_t01",
"Hero:hid_constructor_009_vr_t01",
"Hero:hid_constructor_010_sr_t01",
"Hero:hid_constructor_010_vr_t01",
"Hero:hid_constructor_basebig_sr_t01",
"Hero:hid_constructor_basehyper_r_t01",
"Hero:hid_constructor_basehyper_sr_t01",
"Hero:hid_constructor_basehyper_vr_t01",
"Hero:hid_constructor_basehyperhw_sr_t01",
"Hero:hid_constructor_hammerplasma_sr_t01",
"Hero:hid_constructor_hammerplasma_vr_t01",
"Hero:hid_constructor_hammertank_r_t01",
"Hero:hid_constructor_hammertank_sr_t01",
"Hero:hid_constructor_hammertank_uc_t01",
"Hero:hid_constructor_hammertank_vr_t01",
"Hero:hid_constructor_plasmadamage_r_t01",
"Hero:hid_constructor_plasmadamage_sr_t01",
"Hero:hid_constructor_plasmadamage_vr_t01",
"Hero:hid_constructor_rushbase_r_t01",
"Hero:hid_constructor_rushbase_sr_t01",
"Hero:hid_constructor_rushbase_uc_t01",
"Hero:hid_constructor_rushbase_vr_t01",
"Hero:hid_constructor_sony_r_t01",
"Hero:hid_ninja_007_r_t01",
"Hero:hid_ninja_007_sr_t01",
"Hero:hid_ninja_007_uc_t01",
"Hero:hid_ninja_007_vr_t01",
"Hero:hid_ninja_008_r_t01",
"Hero:hid_ninja_008_sr_t01",
"Hero:hid_ninja_008_vr_t01",
"Hero:hid_ninja_009_r_t01",
"Hero:hid_ninja_009_sr_t01",
"Hero:hid_ninja_009_vr_t01",
"Hero:hid_ninja_010_sr_t01",
"Hero:hid_ninja_010_vr_t01",
"Hero:hid_ninja_slashbreath_r_t01",
"Hero:hid_ninja_slashbreath_sr_t01",
"Hero:hid_ninja_slashbreath_vr_t01",
"Hero:hid_ninja_slashtail_r_t01",
"Hero:hid_ninja_slashtail_sr_t01",
"Hero:hid_ninja_slashtail_uc_t01",
"Hero:hid_ninja_slashtail_vr_t01",
"Hero:hid_ninja_smokedimmak_r_t01",
"Hero:hid_ninja_smokedimmak_sr_t01",
"Hero:hid_ninja_smokedimmak_vr_t01",
"Hero:hid_ninja_sony_r_t01",
"Hero:hid_ninja_starsassassin_foundersf_sr_t01",
"Hero:hid_ninja_starsassassin_foundersm_sr_t01",
"Hero:hid_ninja_starsassassin_r_t01",
"Hero:hid_ninja_starsassassin_sr_t01",
"Hero:hid_ninja_starsassassin_uc_t01",
"Hero:hid_ninja_starsassassin_vr_t01",
"Hero:hid_ninja_starsrain_sr_t01",
"Hero:hid_ninja_starsrain_vr_t01",
"Hero:hid_ninja_starsrainhw_sr_t01",
"Hero:hid_ninja_swordmaster_sr_t01",
"Hero:hid_outlander_007_r_t01",
"Hero:hid_outlander_007_sr_t01",
"Hero:hid_outlander_007_uc_t01",
"Hero:hid_outlander_007_vr_t01",
"Hero:hid_outlander_008_foundersf_sr_t01",
"Hero:hid_outlander_008_foundersm_sr_t01",
"Hero:hid_outlander_008_r_t01",
"Hero:hid_outlander_008_sr_t01",
"Hero:hid_outlander_008_vr_t01",
"Hero:hid_outlander_009_r_t01",
"Hero:hid_outlander_009_sr_t01",
"Hero:hid_outlander_009_vr_t01",
"Hero:hid_outlander_010_sr_t01",
"Hero:hid_outlander_010_vr_t01",
"Hero:hid_outlander_punchdamage_sr_t01",
"Hero:hid_outlander_punchdamage_vr_t01",
"Hero:hid_outlander_punchphase_r_t01",
"Hero:hid_outlander_punchphase_sr_t01",
"Hero:hid_outlander_punchphase_uc_t01",
"Hero:hid_outlander_punchphase_vr_t01",
"Hero:hid_outlander_sony_r_t01",
"Hero:hid_outlander_spherefragment_r_t01",
"Hero:hid_outlander_spherefragment_sr_t01",
"Hero:hid_outlander_spherefragment_vr_t01",
"Hero:hid_outlander_zonefragment_sr_t01",
"Hero:hid_outlander_zoneharvest_r_t01",
"Hero:hid_outlander_zoneharvest_sr_t01",
"Hero:hid_outlander_zoneharvest_uc_t01",
"Hero:hid_outlander_zoneharvest_vr_t01",
"Hero:hid_outlander_zonepistol_r_t01",
"Hero:hid_outlander_zonepistol_sr_t01",
"Hero:hid_outlander_zonepistol_vr_t01",
"Hero:hid_outlander_zonepistolhw_sr_t01",
"Schematic:sid_assault_auto_c_ore_t01",
"Schematic:sid_assault_auto_founders_sr_ore_t01",
"Schematic:sid_assault_auto_halloween_sr_ore_t01",
"Schematic:sid_assault_auto_r_ore_t01",
"Schematic:sid_assault_auto_sr_ore_t01",
"Schematic:sid_assault_auto_uc_ore_t01",
"Schematic:sid_assault_auto_vr_ore_t01",
"Schematic:sid_assault_burst_c_ore_t01",
"Schematic:sid_assault_burst_r_ore_t01",
"Schematic:sid_assault_burst_sr_ore_t01",
"Schematic:sid_assault_burst_uc_ore_t01",
"Schematic:sid_assault_burst_vr_ore_t01",
"Schematic:sid_assault_doubleshot_sr_ore_t01",
"Schematic:sid_assault_doubleshot_vr_ore_t01",
"Schematic:sid_assault_hydra_sr_ore_t01",
"Schematic:sid_assault_lmg_drum_founders_sr_ore_t01",
"Schematic:sid_assault_lmg_drum_founders_vr_ore_t01",
"Schematic:sid_assault_lmg_r_ore_t01",
"Schematic:sid_assault_lmg_sr_ore_t01",
"Schematic:sid_assault_lmg_vr_ore_t01",
"Schematic:sid_assault_raygun_sr_ore_t01",
"Schematic:sid_assault_raygun_vr_ore_t01",
"Schematic:sid_assault_semiauto_c_ore_t01",
"Schematic:sid_assault_semiauto_founders_vr_ore_t01",
"Schematic:sid_assault_semiauto_r_ore_t01",
"Schematic:sid_assault_semiauto_sr_ore_t01",
"Schematic:sid_assault_semiauto_uc_ore_t01",
"Schematic:sid_assault_semiauto_vr_ore_t01",
"Schematic:sid_assault_singleshot_r_ore_t01",
"Schematic:sid_assault_singleshot_sr_ore_t01",
"Schematic:sid_assault_singleshot_vr_ore_t01",
"Schematic:sid_assault_surgical_drum_founders_r_ore_t01",
"Schematic:sid_assault_surgical_sr_ore_t01",
"Schematic:sid_assault_surgical_vr_ore_t01",
"Schematic:sid_blunt_club_light_sr_ore_t01",
"Schematic:sid_blunt_club_light_vr_ore_t01",
"Schematic:sid_blunt_hammer_heavy_c_ore_t01",
"Schematic:sid_blunt_hammer_heavy_founders_vr_ore_t01",
"Schematic:sid_blunt_hammer_heavy_r_ore_t01",
"Schematic:sid_blunt_hammer_heavy_sr_ore_t01",
"Schematic:sid_blunt_hammer_heavy_uc_ore_t01",
"Schematic:sid_blunt_hammer_heavy_vr_ore_t01",
"Schematic:sid_blunt_hammer_rocket_sr_ore_t01",
"Schematic:sid_blunt_hammer_rocket_vr_ore_t01",
"Schematic:sid_blunt_heavy_paddle_c_ore_t01",
"Schematic:sid_blunt_heavy_paddle_r_ore_t01",
"Schematic:sid_blunt_heavy_paddle_uc_ore_t01",
"Schematic:sid_blunt_light_bat_r_ore_t01",
"Schematic:sid_blunt_light_bat_uc_ore_t01",
"Schematic:sid_blunt_light_c_ore_t01",
"Schematic:sid_blunt_light_r_ore_t01",
"Schematic:sid_blunt_light_rocketbat_sr_ore_t01",
"Schematic:sid_blunt_light_rocketbat_vr_ore_t01",
"Schematic:sid_blunt_light_sr_ore_t01",
"Schematic:sid_blunt_light_uc_ore_t01",
"Schematic:sid_blunt_light_vr_ore_t01",
"Schematic:sid_blunt_medium_c_ore_t01",
"Schematic:sid_blunt_medium_r_ore_t01",
"Schematic:sid_blunt_medium_sr_ore_t01",
"Schematic:sid_blunt_medium_uc_ore_t01",
"Schematic:sid_blunt_medium_vr_ore_t01",
"Schematic:sid_blunt_tool_light_r_ore_t01",
"Schematic:sid_blunt_tool_light_uc_ore_t01",
"Schematic:sid_ceiling_electric_aoe_r_t01",
"Schematic:sid_ceiling_electric_aoe_sr_t01",
"Schematic:sid_ceiling_electric_aoe_vr_t01",
"Schematic:sid_ceiling_electric_single_c_t01",
"Schematic:sid_ceiling_electric_single_r_t01",
"Schematic:sid_ceiling_electric_single_sr_t01",
"Schematic:sid_ceiling_electric_single_uc_t01",
"Schematic:sid_ceiling_electric_single_vr_t01",
"Schematic:sid_ceiling_gas_r_t01",
"Schematic:sid_ceiling_gas_sr_t01",
"Schematic:sid_ceiling_gas_uc_t01",
"Schematic:sid_ceiling_gas_vr_t01",
"Schematic:sid_edged_axe_heavy_c_ore_t01",
"Schematic:sid_edged_axe_heavy_r_ore_t01",
"Schematic:sid_edged_axe_heavy_sr_ore_t01",
"Schematic:sid_edged_axe_heavy_uc_ore_t01",
"Schematic:sid_edged_axe_heavy_vr_ore_t01",
"Schematic:sid_edged_axe_light_c_ore_t01",
"Schematic:sid_edged_axe_light_r_ore_t01",
"Schematic:sid_edged_axe_light_sr_ore_t01",
"Schematic:sid_edged_axe_light_uc_ore_t01",
"Schematic:sid_edged_axe_light_vr_ore_t01",
"Schematic:sid_edged_axe_medium_c_ore_t01",
"Schematic:sid_edged_axe_medium_founders_vr_ore_t01",
"Schematic:sid_edged_axe_medium_laser_sr_ore_t01",
"Schematic:sid_edged_axe_medium_laser_vr_ore_t01",
"Schematic:sid_edged_axe_medium_r_ore_t01",
"Schematic:sid_edged_axe_medium_sr_ore_t01",
"Schematic:sid_edged_axe_medium_uc_ore_t01",
"Schematic:sid_edged_axe_medium_vr_ore_t01",
"Schematic:sid_edged_scythe_c_ore_t01",
"Schematic:sid_edged_scythe_laser_sr_ore_t01",
"Schematic:sid_edged_scythe_laser_vr_ore_t01",
"Schematic:sid_edged_scythe_r_ore_t01",
"Schematic:sid_edged_scythe_sr_ore_t01",
"Schematic:sid_edged_scythe_uc_ore_t01",
"Schematic:sid_edged_scythe_vr_ore_t01",
"Schematic:sid_edged_sword_heavy_c_ore_t01",
"Schematic:sid_edged_sword_heavy_founders_vr_ore_t01",
"Schematic:sid_edged_sword_heavy_r_ore_t01",
"Schematic:sid_edged_sword_heavy_sr_ore_t01",
"Schematic:sid_edged_sword_heavy_uc_ore_t01",
"Schematic:sid_edged_sword_heavy_vr_ore_t01",
"Schematic:sid_edged_sword_hydraulic_sr_ore_t01",
"Schematic:sid_edged_sword_hydraulic_vr_ore_t01",
"Schematic:sid_edged_sword_light_c_ore_t01",
"Schematic:sid_edged_sword_light_founders_vr_ore_t01",
"Schematic:sid_edged_sword_light_r_ore_t01",
"Schematic:sid_edged_sword_light_sr_ore_t01",
"Schematic:sid_edged_sword_light_uc_ore_t01",
"Schematic:sid_edged_sword_light_vr_ore_t01",
"Schematic:sid_edged_sword_medium_c_ore_t01",
"Schematic:sid_edged_sword_medium_laser_founders_r_ore_t01",
"Schematic:sid_edged_sword_medium_laser_founders_sr_ore_t01",
"Schematic:sid_edged_sword_medium_laser_founders_vr_ore_t01",
"Schematic:sid_edged_sword_medium_laser_sr_ore_t01",
"Schematic:sid_edged_sword_medium_laser_vr_ore_t01",
"Schematic:sid_edged_sword_medium_r_ore_t01",
"Schematic:sid_edged_sword_medium_sr_ore_t01",
"Schematic:sid_edged_sword_medium_uc_ore_t01",
"Schematic:sid_edged_sword_medium_vr_ore_t01",
"Schematic:sid_floor_health_r_t01",
"Schematic:sid_floor_health_sr_t01",
"Schematic:sid_floor_health_uc_t01",
"Schematic:sid_floor_health_vr_t01",
"Schematic:sid_floor_launcher_r_t01",
"Schematic:sid_floor_launcher_sr_t01",
"Schematic:sid_floor_launcher_uc_t01",
"Schematic:sid_floor_launcher_vr_t01",
"Schematic:sid_floor_spikes_r_t01",
"Schematic:sid_floor_spikes_sr_t01",
"Schematic:sid_floor_spikes_uc_t01",
"Schematic:sid_floor_spikes_vr_t01",
"Schematic:sid_floor_spikes_wood_c_t01",
"Schematic:sid_floor_spikes_wood_r_t01",
"Schematic:sid_floor_spikes_wood_sr_t01",
"Schematic:sid_floor_spikes_wood_uc_t01",
"Schematic:sid_floor_spikes_wood_vr_t01",
"Schematic:sid_floor_ward_r_t01",
"Schematic:sid_floor_ward_sr_t01",
"Schematic:sid_floor_ward_uc_t01",
"Schematic:sid_floor_ward_vr_t01",
"Schematic:sid_launcher_grenade_r_ore_t01",
"Schematic:sid_launcher_grenade_sr_ore_t01",
"Schematic:sid_launcher_grenade_vr_ore_t01",
"Schematic:sid_launcher_hydraulic_sr_ore_t01",
"Schematic:sid_launcher_hydraulic_vr_ore_t01",
"Schematic:sid_launcher_pumpkin_rpg_sr_ore_t01",
"Schematic:sid_launcher_rocket_r_ore_t01",
"Schematic:sid_launcher_rocket_sr_ore_t01",
"Schematic:sid_launcher_rocket_vr_ore_t01",
"Schematic:sid_piercing_spear_c_ore_t01",
"Schematic:sid_piercing_spear_laser_sr_ore_t01",
"Schematic:sid_piercing_spear_laser_vr_ore_t01",
"Schematic:sid_piercing_spear_military_r_ore_t01",
"Schematic:sid_piercing_spear_military_sr_ore_t01",
"Schematic:sid_piercing_spear_military_vr_ore_t01",
"Schematic:sid_piercing_spear_r_ore_t01",
"Schematic:sid_piercing_spear_sr_ore_t01",
"Schematic:sid_piercing_spear_uc_ore_t01",
"Schematic:sid_piercing_spear_vr_ore_t01",
"Schematic:sid_pistol_auto_c_ore_t01",
"Schematic:sid_pistol_auto_r_ore_t01",
"Schematic:sid_pistol_auto_sr_ore_t01",
"Schematic:sid_pistol_auto_uc_ore_t01",
"Schematic:sid_pistol_auto_vr_ore_t01",
"Schematic:sid_pistol_autoheavy_founders_r_ore_t01",
"Schematic:sid_pistol_autoheavy_founders_sr_ore_t01",
"Schematic:sid_pistol_autoheavy_founders_vr_ore_t01",
"Schematic:sid_pistol_autoheavy_r_ore_t01",
"Schematic:sid_pistol_autoheavy_sr_ore_t01",
"Schematic:sid_pistol_autoheavy_vr_ore_t01",
"Schematic:sid_pistol_bolt_sr_ore_t01",
"Schematic:sid_pistol_bolt_vr_ore_t01",
"Schematic:sid_pistol_boltrevolver_c_ore_t01",
"Schematic:sid_pistol_boltrevolver_r_ore_t01",
"Schematic:sid_pistol_boltrevolver_uc_ore_t01",
"Schematic:sid_pistol_dragon_sr_ore_t01",
"Schematic:sid_pistol_dragon_vr_ore_t01",
"Schematic:sid_pistol_firecracker_r_ore_t01",
"Schematic:sid_pistol_firecracker_sr_ore_t01",
"Schematic:sid_pistol_firecracker_vr_ore_t01",
"Schematic:sid_pistol_gatling_sr_ore_t01",
"Schematic:sid_pistol_gatling_vr_ore_t01",
"Schematic:sid_pistol_handcannon_founders_vr_ore_t01",
"Schematic:sid_pistol_handcannon_r_ore_t01",
"Schematic:sid_pistol_handcannon_semi_r_ore_t01",
"Schematic:sid_pistol_handcannon_semi_sr_ore_t01",
"Schematic:sid_pistol_handcannon_semi_vr_ore_t01",
"Schematic:sid_pistol_handcannon_sr_ore_t01",
"Schematic:sid_pistol_handcannon_vr_ore_t01",
"Schematic:sid_pistol_hydraulic_sr_ore_t01",
"Schematic:sid_pistol_hydraulic_vr_ore_t01",
"Schematic:sid_pistol_rapid_founders_vr_ore_t01",
"Schematic:sid_pistol_rapid_r_ore_t01",
"Schematic:sid_pistol_rapid_sr_ore_t01",
"Schematic:sid_pistol_rapid_vr_ore_t01",
"Schematic:sid_pistol_rocket_sr_ore_t01",
"Schematic:sid_pistol_semiauto_c_ore_t01",
"Schematic:sid_pistol_semiauto_founders_vr_ore_t01",
"Schematic:sid_pistol_semiauto_r_ore_t01",
"Schematic:sid_pistol_semiauto_sr_ore_t01",
"Schematic:sid_pistol_semiauto_uc_ore_t01",
"Schematic:sid_pistol_semiauto_vr_ore_t01",
"Schematic:sid_pistol_sixshooter_c_ore_t01",
"Schematic:sid_pistol_sixshooter_r_ore_t01",
"Schematic:sid_pistol_sixshooter_uc_ore_t01",
"Schematic:sid_pistol_space_sr_ore_t01",
"Schematic:sid_pistol_space_vr_ore_t01",
"Schematic:sid_pistol_zapper_sr_ore_t01",
"Schematic:sid_pistol_zapper_vr_ore_t01",
"Schematic:sid_shotgun_auto_founders_vr_ore_t01",
"Schematic:sid_shotgun_auto_r_ore_t01",
"Schematic:sid_shotgun_auto_sr_ore_t01",
"Schematic:sid_shotgun_auto_uc_ore_t01",
"Schematic:sid_shotgun_auto_vr_ore_t01",
"Schematic:sid_shotgun_break_c_ore_t01",
"Schematic:sid_shotgun_break_ou_r_ore_t01",
"Schematic:sid_shotgun_break_ou_sr_ore_t01",
"Schematic:sid_shotgun_break_ou_uc_ore_t01",
"Schematic:sid_shotgun_break_ou_vr_ore_t01",
"Schematic:sid_shotgun_break_r_ore_t01",
"Schematic:sid_shotgun_break_sr_ore_t01",
"Schematic:sid_shotgun_break_uc_ore_t01",
"Schematic:sid_shotgun_break_vr_ore_t01",
"Schematic:sid_shotgun_heavy_sr_ore_t01",
"Schematic:sid_shotgun_longarm_sr_ore_t01",
"Schematic:sid_shotgun_longarm_vr_ore_t01",
"Schematic:sid_shotgun_minigun_sr_ore_t01",
"Schematic:sid_shotgun_semiauto_r_ore_t01",
"Schematic:sid_shotgun_semiauto_sr_ore_t01",
"Schematic:sid_shotgun_semiauto_uc_ore_t01",
"Schematic:sid_shotgun_semiauto_vr_ore_t01",
"Schematic:sid_shotgun_standard_c_ore_t01",
"Schematic:sid_shotgun_standard_r_ore_t01",
"Schematic:sid_shotgun_standard_sr_ore_t01",
"Schematic:sid_shotgun_standard_uc_ore_t01",
"Schematic:sid_shotgun_standard_vr_ore_t01",
"Schematic:sid_shotgun_tactical_c_ore_t01",
"Schematic:sid_shotgun_tactical_founders_r_ore_t01",
"Schematic:sid_shotgun_tactical_founders_sr_ore_t01",
"Schematic:sid_shotgun_tactical_founders_vr_ore_t01",
"Schematic:sid_shotgun_tactical_precision_r_ore_t01",
"Schematic:sid_shotgun_tactical_precision_sr_ore_t01",
"Schematic:sid_shotgun_tactical_precision_vr_ore_t01",
"Schematic:sid_shotgun_tactical_r_ore_t01",
"Schematic:sid_shotgun_tactical_uc_ore_t01",
"Schematic:sid_sniper_amr_r_ore_t01",
"Schematic:sid_sniper_amr_sr_ore_t01",
"Schematic:sid_sniper_amr_vr_ore_t01",
"Schematic:sid_sniper_auto_founders_vr_ore_t01",
"Schematic:sid_sniper_auto_r_ore_t01",
"Schematic:sid_sniper_auto_sr_ore_t01",
"Schematic:sid_sniper_auto_uc_ore_t01",
"Schematic:sid_sniper_auto_vr_ore_t01",
"Schematic:sid_sniper_boltaction_c_ore_t01",
"Schematic:sid_sniper_boltaction_r_ore_t01",
"Schematic:sid_sniper_boltaction_scope_r_ore_t01",
"Schematic:sid_sniper_boltaction_scope_sr_ore_t01",
"Schematic:sid_sniper_boltaction_scope_vr_ore_t01",
"Schematic:sid_sniper_boltaction_uc_ore_t01",
"Schematic:sid_sniper_hydraulic_sr_ore_t01",
"Schematic:sid_sniper_hydraulic_vr_ore_t01",
"Schematic:sid_sniper_shredder_sr_ore_t01",
"Schematic:sid_sniper_shredder_vr_ore_t01",
"Schematic:sid_sniper_standard_c_ore_t01",
"Schematic:sid_sniper_standard_founders_vr_ore_t01",
"Schematic:sid_sniper_standard_r_ore_t01",
"Schematic:sid_sniper_standard_scope_sr_ore_t01",
"Schematic:sid_sniper_standard_scope_vr_ore_t01",
"Schematic:sid_sniper_standard_sr_ore_t01",
"Schematic:sid_sniper_standard_uc_ore_t01",
"Schematic:sid_sniper_standard_vr_ore_t01",
"Schematic:sid_sniper_tripleshot_sr_ore_t01",
"Schematic:sid_sniper_tripleshot_vr_ore_t01",
"Schematic:sid_wall_darts_r_t01",
"Schematic:sid_wall_darts_sr_t01",
"Schematic:sid_wall_darts_uc_t01",
"Schematic:sid_wall_darts_vr_t01",
"Schematic:sid_wall_electric_r_t01",
"Schematic:sid_wall_electric_sr_t01",
"Schematic:sid_wall_electric_uc_t01",
"Schematic:sid_wall_electric_vr_t01",
"Schematic:sid_wall_launcher_r_t01",
"Schematic:sid_wall_launcher_sr_t01",
"Schematic:sid_wall_launcher_uc_t01",
"Schematic:sid_wall_launcher_vr_t01",
"Schematic:sid_wall_light_r_t01",
"Schematic:sid_wall_light_sr_t01",
"Schematic:sid_wall_light_vr_t01",
"Schematic:sid_wall_wood_spikes_c_t01",
"Schematic:sid_wall_wood_spikes_r_t01",
"Schematic:sid_wall_wood_spikes_sr_t01",
"Schematic:sid_wall_wood_spikes_uc_t01",
"Schematic:sid_wall_wood_spikes_vr_t01",
"Worker:managerdoctor_c_t01",
"Worker:managerdoctor_r_t01",
"Worker:managerdoctor_sr_kingsly_t01",
"Worker:managerdoctor_sr_noctor_t01",
"Worker:managerdoctor_sr_treky_t01",
"Worker:managerdoctor_uc_t01",
"Worker:managerdoctor_vr_t01",
"Worker:managerengineer_c_t01",
"Worker:managerengineer_r_t01",
"Worker:managerengineer_sr_countess_t01",
"Worker:managerengineer_sr_maths_t01",
"Worker:managerengineer_sr_sobs_t01",
"Worker:managerengineer_uc_t01",
"Worker:managerengineer_vr_t01",
"Worker:managerexplorer_c_t01",
"Worker:managerexplorer_r_t01",
"Worker:managerexplorer_sr_birdie_t01",
"Worker:managerexplorer_sr_eagle_t01",
"Worker:managerexplorer_sr_spacebound_t01",
"Worker:managerexplorer_uc_t01",
"Worker:managerexplorer_vr_t01",
"Worker:managergadgeteer_c_t01",
"Worker:managergadgeteer_r_t01",
"Worker:managergadgeteer_sr_fixer_t01",
"Worker:managergadgeteer_sr_flak_t01",
"Worker:managergadgeteer_sr_zapps_t01",
"Worker:managergadgeteer_uc_t01",
"Worker:managergadgeteer_vr_t01",
"Worker:managerinventor_c_t01",
"Worker:managerinventor_r_t01",
"Worker:managerinventor_sr_frequency_t01",
"Worker:managerinventor_sr_rad_t01",
"Worker:managerinventor_sr_square_t01",
"Worker:managerinventor_uc_t01",
"Worker:managerinventor_vr_t01",
"Worker:managermartialartist_c_t01",
"Worker:managermartialartist_r_t01",
"Worker:managermartialartist_sr_dragon_t01",
"Worker:managermartialartist_sr_samurai_t01",
"Worker:managermartialartist_sr_tiger_t01",
"Worker:managermartialartist_uc_t01",
"Worker:managermartialartist_vr_t01",
"Worker:managersoldier_c_t01",
"Worker:managersoldier_r_t01",
"Worker:managersoldier_sr_malcolm_t01",
"Worker:managersoldier_sr_princess_t01",
"Worker:managersoldier_sr_ramsie_t01",
"Worker:managersoldier_uc_t01",
"Worker:managersoldier_vr_t01",
"Worker:managertrainer_c_t01",
"Worker:managertrainer_r_t01",
"Worker:managertrainer_sr_jumpy_t01",
"Worker:managertrainer_sr_raider_t01",
"Worker:managertrainer_sr_yoglattes_t01",
"Worker:managertrainer_uc_t01",
"Worker:managertrainer_vr_t01",
"Worker:workerbasic_c_t01",
"Worker:workerbasic_r_t01",
"Worker:workerbasic_sr_t01",
"Worker:workerbasic_uc_t01",
"Worker:workerbasic_vr_t01",
"Worker:workerhalloween_alt_sr_t01",
"Worker:workerhalloween_c_t01",
"Worker:workerhalloween_r_t01",
"Worker:workerhalloween_sr_t01",
"Worker:workerhalloween_uc_t01",
"Worker:workerhalloween_vr_t01"
]

View File

@@ -1,10 +0,0 @@
{
"friends": [],
"incoming": [],
"outgoing": [],
"suggested": [],
"blocklist": [],
"settings": {
"acceptInvites": "public"
}
}

View File

@@ -0,0 +1,28 @@
# Do not remove/change, this redirects epicgames xmpp to lawinserver xmpp
[OnlineSubsystemMcp.Xmpp]
bUseSSL=false
ServerAddr="ws://127.0.0.1"
ServerPort=80
# Do not remove/change, this redirects epicgames xmpp to lawinserver xmpp
[OnlineSubsystemMcp.Xmpp Prod]
bUseSSL=false
ServerAddr="ws://127.0.0.1"
ServerPort=80
# Forces fortnite to use the v1 party system to support lawinserver xmpp
[OnlineSubsystemMcp]
bUsePartySystemV2=false
# Forces fortnite to use the v1 party system to support lawinserver xmpp
[OnlineSubsystemMcp.OnlinePartySystemMcpAdapter]
bUsePartySystemV2=false
# Fix for XMPP not working on some versions
[XMPP]
bEnableWebsockets=true
# Fix for long waiting at checking connections to datacenters on Switch & Mobile
[/Script/Qos.QosRegionManager]
NumTestsPerRegion=1
PingTimeout=0.1

View File

@@ -23,3 +23,4 @@ bIsAthenaGlobalChatEnabled=true # Battle royale global chat.
[/Script/FortniteGame.FortOnlineAccount] [/Script/FortniteGame.FortOnlineAccount]
bEnableEulaCheck=false bEnableEulaCheck=false
bShouldCheckIfPlatformAllowed=false

View File

@@ -0,0 +1,3 @@
[/Script/Engine.InputSettings]
+ConsoleKeys=Tilde # Enables console using the tilde key
+ConsoleKeys=F8 # Enables console using the F8 key

View File

@@ -70,3 +70,10 @@ cubeSpawnDate=2020-01-01T00:00:00.000Z
## Blockbuster Contest winner video at Risky Reels event. (v5.30 Only)* ## Blockbuster Contest winner video at Risky Reels event. (v5.30 Only)*
bEnableBlockbusterRiskyEvent=false bEnableBlockbusterRiskyEvent=false
## Cube melting in Loot Lake event. (v5.41 Only)*
# When set to true, the cube will fall into Loot Lake and start melting on the date specified below.
# After the event, Loot Lake will remain purple in new matches.
bEnableCubeLake=false
cubeLakeDate=2020-01-01T00:00:00.000Z

View File

@@ -44189,6 +44189,98 @@
}, },
"quantity": 1 "quantity": 1
}, },
"Schematic:SID_Edged_IceSword_SR_Crystal_T05": {
"templateId": "Schematic:SID_Edged_IceSword_SR_Crystal_T05",
"attributes": {
"legacy_alterations": [],
"max_level_bonus": 0,
"level": 50,
"refund_legacy_item": false,
"item_seen": true,
"alterations": [
"",
"",
"",
"",
"",
""
],
"xp": 0,
"refundable": false,
"alteration_base_rarities": [],
"favorite": false
},
"quantity": 1
},
"Schematic:SID_Edged_IceSword_SR_Ore_T05": {
"templateId": "Schematic:SID_Edged_IceSword_SR_Ore_T05",
"attributes": {
"legacy_alterations": [],
"max_level_bonus": 0,
"level": 50,
"refund_legacy_item": false,
"item_seen": true,
"alterations": [
"",
"",
"",
"",
"",
""
],
"xp": 0,
"refundable": false,
"alteration_base_rarities": [],
"favorite": false
},
"quantity": 1
},
"Schematic:SID_Edged_IceSword_VR_Crystal_T05": {
"templateId": "Schematic:SID_Edged_IceSword_VR_Crystal_T05",
"attributes": {
"legacy_alterations": [],
"max_level_bonus": 0,
"level": 50,
"refund_legacy_item": false,
"item_seen": true,
"alterations": [
"",
"",
"",
"",
"",
""
],
"xp": 0,
"refundable": false,
"alteration_base_rarities": [],
"favorite": false
},
"quantity": 1
},
"Schematic:SID_Edged_IceSword_VR_Ore_T05": {
"templateId": "Schematic:SID_Edged_IceSword_VR_Ore_T05",
"attributes": {
"legacy_alterations": [],
"max_level_bonus": 0,
"level": 50,
"refund_legacy_item": false,
"item_seen": true,
"alterations": [
"",
"",
"",
"",
"",
""
],
"xp": 0,
"refundable": false,
"alteration_base_rarities": [],
"favorite": false
},
"quantity": 1
},
"Schematic:Ingredient_Duct_Tape": { "Schematic:Ingredient_Duct_Tape": {
"templateId": "Schematic:Ingredient_Duct_Tape", "templateId": "Schematic:Ingredient_Duct_Tape",
"attributes": { "attributes": {

View File

@@ -2,7 +2,7 @@
"_id": "LawinServer", "_id": "LawinServer",
"created": "0001-01-01T00:00:00.000Z", "created": "0001-01-01T00:00:00.000Z",
"updated": "0001-01-01T00:00:00.000Z", "updated": "0001-01-01T00:00:00.000Z",
"rvn": 1, "rvn": 10,
"wipeNumber": 1, "wipeNumber": 1,
"accountId": "LawinServer", "accountId": "LawinServer",
"profileId": "profile0", "profileId": "profile0",
@@ -35669,6 +35669,345 @@
"favorite": false "favorite": false
}, },
"quantity": 1 "quantity": 1
},
"507ac4fc-4c0e-412d-8f99-b7dee58e2f76": {
"templateId": "Expedition:expedition_sea_survivorscouting_short_t01",
"attributes": {
"expedition_expiration_end_time": "2024-05-23T15:33:33.934Z",
"expedition_criteria": [
"RequiresRareCommando"
],
"level": 1,
"expedition_max_target_power": 120,
"expedition_min_target_power": 6,
"expedition_slot_id": "expedition.generation.sea.t01_0",
"expedition_expiration_start_time": "2024-05-23T14:03:33.934Z"
},
"quantity": 1
},
"a4b374c2-4e4a-4d1a-b549-6e979f20b415": {
"templateId": "Expedition:expedition_sea_survivorscouting_short_t01",
"attributes": {
"expedition_expiration_end_time": "2024-05-23T15:33:33.934Z",
"expedition_criteria": [],
"level": 1,
"expedition_max_target_power": 120,
"expedition_min_target_power": 6,
"expedition_slot_id": "expedition.generation.sea.t01_1",
"expedition_expiration_start_time": "2024-05-23T14:03:33.934Z"
},
"quantity": 1
},
"d73a42be-10cc-4175-823a-b0330ebdac5e": {
"templateId": "Expedition:expedition_air_supplyrun_long_t02",
"attributes": {
"expedition_expiration_end_time": "2024-05-23T15:33:33.934Z",
"expedition_criteria": [],
"level": 1,
"expedition_max_target_power": 330,
"expedition_min_target_power": 16,
"expedition_slot_id": "expedition.generation.air.t02_0",
"expedition_expiration_start_time": "2024-05-23T14:03:33.934Z"
},
"quantity": 1
},
"1eac710f-cf80-4de2-bc46-54426a2e3c90": {
"templateId": "Expedition:expedition_air_survivorscouting_long_t02",
"attributes": {
"expedition_expiration_end_time": "2024-05-23T15:33:33.934Z",
"expedition_criteria": [
"RequiresLegendaryConstructor"
],
"level": 1,
"expedition_max_target_power": 385,
"expedition_min_target_power": 19,
"expedition_slot_id": "expedition.generation.air.t02_1",
"expedition_expiration_start_time": "2024-05-23T14:03:33.934Z"
},
"quantity": 1
},
"fabc7237-4d92-4692-8214-82b02aac8514": {
"templateId": "Expedition:expedition_resourcerun_stone_short",
"attributes": {
"expedition_expiration_end_time": "2024-05-23T15:33:33.934Z",
"expedition_criteria": [],
"level": 1,
"expedition_max_target_power": 110,
"expedition_min_target_power": 5,
"expedition_slot_id": "expedition.generation.land.t02_0",
"expedition_expiration_start_time": "2024-05-23T14:03:33.934Z"
},
"quantity": 1
},
"1326df1e-fe4d-44d0-aaf9-a86107ff3b76": {
"templateId": "Expedition:expedition_survivorscouting_medium_t02",
"attributes": {
"expedition_expiration_end_time": "2024-05-23T15:33:33.934Z",
"expedition_criteria": [
"RequiresRareConstructor"
],
"level": 1,
"expedition_max_target_power": 250,
"expedition_min_target_power": 12,
"expedition_slot_id": "expedition.generation.land.t02_1",
"expedition_expiration_start_time": "2024-05-23T14:03:33.934Z"
},
"quantity": 1
},
"7888975d-08d3-47c7-8c72-2adc91a0138e": {
"templateId": "Expedition:expedition_sea_supplyrun_medium_t02",
"attributes": {
"expedition_expiration_end_time": "2024-05-23T15:33:33.934Z",
"expedition_criteria": [],
"level": 1,
"expedition_max_target_power": 215,
"expedition_min_target_power": 10,
"expedition_slot_id": "expedition.generation.sea.t02_0",
"expedition_expiration_start_time": "2024-05-23T14:03:33.934Z"
},
"quantity": 1
},
"d068b9f2-429d-4d5a-9c0b-5591f13ab22e": {
"templateId": "Expedition:expedition_sea_supplyrun_medium_t02",
"attributes": {
"expedition_expiration_end_time": "2024-05-23T15:33:33.934Z",
"expedition_criteria": [
"RequiresLegendaryNinja"
],
"level": 1,
"expedition_max_target_power": 215,
"expedition_min_target_power": 10,
"expedition_slot_id": "expedition.generation.sea.t02_1",
"expedition_expiration_start_time": "2024-05-23T14:03:33.934Z"
},
"quantity": 1
},
"ae374104-d124-4f8d-8d9e-6a309efe4271": {
"templateId": "Expedition:expedition_sea_survivorscouting_medium_t03",
"attributes": {
"expedition_expiration_end_time": "2024-05-23T15:33:33.934Z",
"expedition_criteria": [
"RequiresEpicCommando"
],
"level": 1,
"expedition_max_target_power": 370,
"expedition_min_target_power": 18,
"expedition_slot_id": "expedition.generation.sea.t03_0",
"expedition_expiration_start_time": "2024-05-23T14:03:33.934Z"
},
"quantity": 1
},
"ca25b234-b23f-4317-a54e-ff8492f6a18b": {
"templateId": "Expedition:expedition_sea_survivorscouting_medium_t03",
"attributes": {
"expedition_expiration_end_time": "2024-05-23T15:33:33.934Z",
"expedition_criteria": [],
"level": 1,
"expedition_max_target_power": 370,
"expedition_min_target_power": 18,
"expedition_slot_id": "expedition.generation.sea.t03_1",
"expedition_expiration_start_time": "2024-05-23T14:03:33.934Z"
},
"quantity": 1
},
"35790c16-d6c8-4aac-b861-bfb6e9356f35": {
"templateId": "Expedition:expedition_air_supplyrun_long_t03",
"attributes": {
"expedition_expiration_end_time": "2024-05-23T15:33:33.934Z",
"expedition_criteria": [
"RequiresLegendaryConstructor"
],
"level": 1,
"expedition_max_target_power": 520,
"expedition_min_target_power": 26,
"expedition_slot_id": "expedition.generation.air.t03_0",
"expedition_expiration_start_time": "2024-05-23T14:03:33.934Z"
},
"quantity": 1
},
"f4f13404-6185-4f2f-8987-f65d0522cae1": {
"templateId": "Expedition:expedition_air_supplyrun_long_t03",
"attributes": {
"expedition_expiration_end_time": "2024-05-23T15:33:33.934Z",
"expedition_criteria": [
"RequiresConstructor"
],
"level": 1,
"expedition_max_target_power": 520,
"expedition_min_target_power": 26,
"expedition_slot_id": "expedition.generation.air.t03_1",
"expedition_expiration_start_time": "2024-05-23T14:03:33.934Z"
},
"quantity": 1
},
"1ddb6b83-1687-4f17-a7f0-580b9b4a6310": {
"templateId": "Expedition:expedition_supplyrun_medium_t03",
"attributes": {
"expedition_expiration_end_time": "2024-05-23T15:33:33.934Z",
"expedition_criteria": [
"RequiresEpicOutlander",
"RequiresRareConstructor"
],
"level": 1,
"expedition_max_target_power": 275,
"expedition_min_target_power": 13,
"expedition_slot_id": "expedition.generation.land.t03_0",
"expedition_expiration_start_time": "2024-05-23T14:03:33.934Z"
},
"quantity": 1
},
"267988c1-de32-4cf5-9aeb-4270779953bc": {
"templateId": "Expedition:expedition_craftingrun_short_t03",
"attributes": {
"expedition_expiration_end_time": "2024-05-23T15:33:33.934Z",
"expedition_criteria": [
"RequiresRareOutlander"
],
"level": 1,
"expedition_max_target_power": 225,
"expedition_min_target_power": 11,
"expedition_slot_id": "expedition.generation.land.t03_1",
"expedition_expiration_start_time": "2024-05-23T14:03:33.934Z"
},
"quantity": 1
},
"c7a44a2f-173c-4aed-b229-4f9a8297d4a8": {
"templateId": "Expedition:expedition_supplyrun_long_t04",
"attributes": {
"expedition_expiration_end_time": "2024-05-23T15:33:33.934Z",
"expedition_criteria": [
"RequiresEpicCommando"
],
"level": 1,
"expedition_max_target_power": 685,
"expedition_min_target_power": 34,
"expedition_slot_id": "expedition.generation.land.t04_0",
"expedition_expiration_start_time": "2024-05-23T14:03:33.934Z"
},
"quantity": 1
},
"9bd9ec9a-2d79-451c-9b95-bfd6345c398c": {
"templateId": "Expedition:expedition_craftingrun_long_t04",
"attributes": {
"expedition_expiration_end_time": "2024-05-23T15:33:33.934Z",
"expedition_criteria": [
"RequiresLegendaryConstructor"
],
"level": 1,
"expedition_max_target_power": 685,
"expedition_min_target_power": 34,
"expedition_slot_id": "expedition.generation.land.t04_1",
"expedition_expiration_start_time": "2024-05-23T14:03:33.934Z"
},
"quantity": 1
},
"e5cdf1d3-3bab-4ab8-9bea-e225b0fb60b3": {
"templateId": "Expedition:expedition_sea_survivorscouting_long_t04",
"attributes": {
"expedition_expiration_end_time": "2024-05-23T15:33:33.934Z",
"expedition_criteria": [],
"level": 1,
"expedition_max_target_power": 825,
"expedition_min_target_power": 41,
"expedition_slot_id": "expedition.generation.sea.t04_0",
"expedition_expiration_start_time": "2024-05-23T14:03:33.934Z"
},
"quantity": 1
},
"663eb718-757b-4e86-bf0e-1002d4f11396": {
"templateId": "Expedition:expedition_sea_survivorscouting_long_t04",
"attributes": {
"expedition_expiration_end_time": "2024-05-23T15:33:33.934Z",
"expedition_criteria": [],
"level": 1,
"expedition_max_target_power": 825,
"expedition_min_target_power": 41,
"expedition_slot_id": "expedition.generation.sea.t04_1",
"expedition_expiration_start_time": "2024-05-23T14:03:33.934Z"
},
"quantity": 1
},
"e948d68d-1c42-480c-ab10-102b16442982": {
"templateId": "Expedition:expedition_air_survivorscouting_long_t04",
"attributes": {
"expedition_expiration_end_time": "2024-05-23T15:33:33.934Z",
"expedition_criteria": [
"RequiresRareConstructor"
],
"level": 1,
"expedition_max_target_power": 835,
"expedition_min_target_power": 41,
"expedition_slot_id": "expedition.generation.air.t04_0",
"expedition_expiration_start_time": "2024-05-23T14:03:33.934Z"
},
"quantity": 1
},
"ea24aa52-dc70-41b5-ba85-83fe81f44d86": {
"templateId": "Expedition:expedition_air_supplyrun_long_t04",
"attributes": {
"expedition_expiration_end_time": "2024-05-23T15:33:33.934Z",
"expedition_criteria": [],
"level": 1,
"expedition_max_target_power": 735,
"expedition_min_target_power": 36,
"expedition_slot_id": "expedition.generation.air.t04_1",
"expedition_expiration_start_time": "2024-05-23T14:03:33.934Z"
},
"quantity": 1
},
"0748bf5d-f770-446b-bb14-5b784b399cdd": {
"templateId": "Expedition:expedition_choppingwood_t00",
"attributes": {
"expedition_expiration_end_time": "2024-05-23T15:33:33.934Z",
"expedition_criteria": [],
"level": 1,
"expedition_max_target_power": 20,
"expedition_min_target_power": 1,
"expedition_slot_id": "expedition.generation.miningore",
"expedition_expiration_start_time": "2024-05-23T14:03:33.934Z"
},
"quantity": 1
},
"ad45e99c-0f8a-4b39-abdb-d2d10daeebcf": {
"templateId": "Expedition:expedition_miningore_t00",
"attributes": {
"expedition_expiration_end_time": "2024-05-23T15:33:33.934Z",
"expedition_criteria": [
"RequiresRareConstructor"
],
"level": 1,
"expedition_max_target_power": 25,
"expedition_min_target_power": 1,
"expedition_slot_id": "expedition.generation.choppingwood",
"expedition_expiration_start_time": "2024-05-23T14:03:33.934Z"
},
"quantity": 1
},
"60b692b1-eae8-4029-8fb0-f7ca5aa20dfa": {
"templateId": "Expedition:expedition_resourcerun_wood_medium",
"attributes": {
"expedition_expiration_end_time": "2024-05-23T15:33:33.934Z",
"expedition_criteria": [],
"level": 1,
"expedition_max_target_power": 100,
"expedition_min_target_power": 5,
"expedition_slot_id": "expedition.generation.land.t01_0",
"expedition_expiration_start_time": "2024-05-23T14:03:33.934Z"
},
"quantity": 1
},
"4f7ffa01-e7d6-4d13-b8b9-f07e404d7128": {
"templateId": "Expedition:expedition_survivorscouting_short_t01",
"attributes": {
"expedition_expiration_end_time": "2024-05-23T15:33:33.934Z",
"expedition_criteria": [],
"level": 1,
"expedition_max_target_power": 130,
"expedition_min_target_power": 6,
"expedition_slot_id": "expedition.generation.land.t01_1",
"expedition_expiration_start_time": "2024-05-23T14:03:33.934Z"
},
"quantity": 1
} }
}, },
"stats": { "stats": {
@@ -35793,12 +36132,12 @@
"level": 10, "level": 10,
"named_counters": { "named_counters": {
"SubGameSelectCount_Campaign": { "SubGameSelectCount_Campaign": {
"current_count": 0, "current_count": 1,
"last_incremented_time": "" "last_incremented_time": "2024-05-23T14:03:33.864Z"
}, },
"SubGameSelectCount_Athena": { "SubGameSelectCount_Athena": {
"current_count": 0, "current_count": 7,
"last_incremented_time": "" "last_incremented_time": "2024-05-23T13:57:38.022Z"
} }
}, },
"default_hero_squad_id": "", "default_hero_squad_id": "",
@@ -35945,5 +36284,5 @@
"packs_granted": 13 "packs_granted": 13
} }
}, },
"commandRevision": 0 "commandRevision": 9
} }

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 60 KiB

View File

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

Before

Width:  |  Height:  |  Size: 117 KiB

After

Width:  |  Height:  |  Size: 117 KiB

View File

Before

Width:  |  Height:  |  Size: 398 KiB

After

Width:  |  Height:  |  Size: 398 KiB

View File

Before

Width:  |  Height:  |  Size: 291 KiB

After

Width:  |  Height:  |  Size: 291 KiB

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More