diff --git a/LICENSE b/LICENSE index 8a26e53..e075628 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2024 Celestial Studioz . +Copyright (c) 2024 YoPhlox . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/art/screenshots/freeplay.png b/art/screenshots/freeplay.png new file mode 100644 index 0000000..2bf4c55 Binary files /dev/null and b/art/screenshots/freeplay.png differ diff --git a/art/screenshots/in-game-downloader.png b/art/screenshots/in-game-downloader.png new file mode 100644 index 0000000..f5daf7a Binary files /dev/null and b/art/screenshots/in-game-downloader.png differ diff --git a/art/screenshots/in-game.png b/art/screenshots/in-game.png new file mode 100644 index 0000000..40ff82d Binary files /dev/null and b/art/screenshots/in-game.png differ diff --git a/art/screenshots/title.png b/art/screenshots/title.png new file mode 100644 index 0000000..40ce16e Binary files /dev/null and b/art/screenshots/title.png differ diff --git a/source/Main.hx b/source/Main.hx index 6015f83..84724b1 100644 --- a/source/Main.hx +++ b/source/Main.hx @@ -91,12 +91,9 @@ class Main extends Sprite { FlxG.autoPause = false; FlxG.mouse.visible = false; - #if desktop Lib.current.loaderInfo.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, onCrash); - #end } - #if desktop function onCrash(e:UncaughtErrorEvent):Void { var errMsg:String = ""; var path:String; @@ -130,5 +127,4 @@ class Main extends Sprite { Application.current.window.alert(errMsg, "Error!"); Sys.exit(1); } - #end } diff --git a/source/Utils.hx b/source/Utils.hx index 91327b6..5a85bca 100644 --- a/source/Utils.hx +++ b/source/Utils.hx @@ -6,7 +6,7 @@ class Utils { // MAIN SWAGGER SHITS! public static var VERSION:String = " v1.0"; static public var soundExt:String = ".mp3"; - public static final discordRpc:String = "1298163987464060978"; /// OG RPC: 1139936005785407578, CURRENT ONE IS SONIC X SHADOW GENS RPC ID + public static final discordRpc:String = "1139936005785407578"; public function create():Void { #if (!web) diff --git a/source/import.hx b/source/import.hx index 1a6ac75..05d9a76 100644 --- a/source/import.hx +++ b/source/import.hx @@ -6,3 +6,4 @@ import states.SwagState; import api.dc.Discord; #end import game.HighScoreManager; +import util.AutoUpdater; \ No newline at end of file diff --git a/source/states/OutdatedState.hx b/source/states/OutdatedState.hx new file mode 100644 index 0000000..863c787 --- /dev/null +++ b/source/states/OutdatedState.hx @@ -0,0 +1,44 @@ +package states; + +import flixel.FlxG; +import flixel.FlxSprite; +import flixel.text.FlxText; +import flixel.util.FlxColor; +import lime.app.Application; + +class OutdatedState extends SwagState +{ + public static var leftState:Bool = false; + + override function create() + { + super.create(); + var bg:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, FlxColor.BLACK); + add(bg); + var ver = "v" + AutoUpdater.CURRENT_VERSION; + var txt:FlxText = new FlxText(0, 0, FlxG.width, + "HEY! You're running an outdated version of Moon4K!\nCurrent version is " + + ver + + " while the most recent version is " + + AutoUpdater.latestVersion + + "! Press Enter/Space to update, or ESCAPE to ignore this!!", + 32); + txt.setFormat("VCR OSD Mono", 32, FlxColor.WHITE, CENTER); + txt.screenCenter(); + add(txt); + } + + override function update(elapsed:Float) + { + if (FlxG.keys.justPressed.ENTER || FlxG.keys.justPressed.SPACE) + { + AutoUpdater.downloadUpdate(); + } + if (FlxG.keys.justPressed.ESCAPE) + { + leftState = true; + transitionState(new MainMenuState()); + } + super.update(elapsed); + } +} \ No newline at end of file diff --git a/source/states/TitleState.hx b/source/states/TitleState.hx index 62b3303..92f8edf 100644 --- a/source/states/TitleState.hx +++ b/source/states/TitleState.hx @@ -5,7 +5,8 @@ import flixel.FlxState; import flixel.text.FlxText; import flixel.util.FlxColor; import flixel.FlxSprite; - +import flixel.util.FlxTimer; +import states.OutdatedState; class TitleState extends SwagState { var titlesprite:FlxSprite; var titleMusic:Array = ['Lofi Music 1', 'Lofi Music 2', 'Trojan Virus v3 - VS Ron']; @@ -38,7 +39,21 @@ class TitleState extends SwagState { override public function update(elapsed:Float):Void { if (FlxG.keys.justPressed.ENTER || FlxG.keys.justPressed.SPACE) { - transitionState(new MainMenuState()); + new FlxTimer().start(2, function(tmr:FlxTimer) + { + // Check if version is outdated + AutoUpdater.checkForUpdates(); + + if (AutoUpdater.isNewerVersion(AutoUpdater.latestVersion, AutoUpdater.CURRENT_VERSION) && !OutdatedState.leftState) + { + trace('OLD VERSION!'); + transitionState(new OutdatedState()); + } + else + { + transitionState(new MainMenuState()); + } + }); } super.update(elapsed); } diff --git a/source/util/AutoUpdater.hx b/source/util/AutoUpdater.hx new file mode 100644 index 0000000..a6fc42b --- /dev/null +++ b/source/util/AutoUpdater.hx @@ -0,0 +1,251 @@ + +/* + * MIT License + * + * Copyright (c) 2024 YoPhlox + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH + * THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +package util; + +import haxe.Http; +import haxe.io.Bytes; +import sys.FileSystem; +import sys.io.File; +import haxe.zip.Reader; +import lime.app.Application; +import flixel.FlxG; +import flixel.ui.FlxButton; +import flixel.text.FlxText; +import flixel.util.FlxColor; +import sys.ssl.Socket; +import haxe.io.BytesOutput; +using StringTools; + +class AutoUpdater +{ + private static inline var VERSION_URL = "https://raw.githubusercontent.com/yophlox/VersionShit/refs/heads/main/Moon4KVer.txt"; // Replace with your repo and txt file + private static inline var DOWNLOAD_URL = "https://github.com/yophlox/Moon4K/releases/latest/download/Moon4K-Release.zip"; // Replace with your repo and zip file + public static inline var CURRENT_VERSION = "1.0"; // Replace with your version + + public static var latestVersion:String = ""; + + public static function checkForUpdates():Void + { + var http = new Http(VERSION_URL); + + http.onData = function(data:String) { + latestVersion = StringTools.trim(data); + if (isNewerVersion(latestVersion, CURRENT_VERSION)) { + showUpdatePrompt(latestVersion); + } + } + + http.onError = function(error) { + trace("Error checking for updates: " + error); + } + + trace("Checking for updates..."); + trace("Latest version: " + latestVersion); + trace("Current version: " + CURRENT_VERSION); + + http.request(); + } + + public static function isNewerVersion(latest:String, current:String):Bool + { + var latestParts = latest.split("."); + var currentParts = current.split("."); + + for (i in 0...3) { + var latestNum = Std.parseInt(latestParts[i]); + var currentNum = Std.parseInt(currentParts[i]); + + if (latestNum > currentNum) return true; + if (latestNum < currentNum) return false; + } + + return false; + } + + private static function showUpdatePrompt(newVersion:String):Void + { + var yesButton:FlxButton; + var noButton:FlxButton; + var promptText:FlxText; + + promptText = new FlxText(0, 0, FlxG.width, 'A new version ($newVersion) is available. Do you want to update?'); + promptText.setFormat(null, 16, FlxColor.WHITE, CENTER); + promptText.screenCenter(Y); + promptText.y -= 50; + + noButton = new FlxButton(FlxG.width / 2 + 20, promptText.y + 100, "No", function() { + removePrompt(promptText, yesButton, noButton); + }); + + yesButton = new FlxButton(FlxG.width / 2 - 100, promptText.y + 100, "Yes", function() { + downloadUpdate(); + removePrompt(promptText, yesButton, noButton); + }); + + FlxG.state.add(promptText); + FlxG.state.add(yesButton); + FlxG.state.add(noButton); + } + + private static function removePrompt(promptText:FlxText, yesButton:FlxButton, noButton:FlxButton):Void + { + FlxG.state.remove(promptText); + FlxG.state.remove(yesButton); + FlxG.state.remove(noButton); + } + + public static function downloadUpdate():Void + { + trace("Attempting to download from: " + DOWNLOAD_URL); + var data = downloadWithRedirects(DOWNLOAD_URL); + if (data != null && data.length > 0) { + handleDownloadedData(data); + } else { + trace("Download failed"); + var errorText = new FlxText(0, 0, FlxG.width, + "Download failed. Please check your internet connection and try again.\n" + + "Error details: Unable to connect to update server.\n" + + "URL: " + DOWNLOAD_URL); + errorText.alignment = CENTER; + errorText.screenCenter(); + FlxG.state.add(errorText); + } + } + + private static function downloadWithRedirects(url:String, redirectCount:Int = 0):Bytes + { + if (redirectCount > 5) { + trace("Too many redirects"); + return null; + } + + try { + var http = new Http(url); + var output = new BytesOutput(); + var result:Bytes = null; + + http.onStatus = function(status:Int) { + trace("HTTP Status: " + status); + if (status >= 300 && status < 400) { + var newUrl = http.responseHeaders.get("Location"); + if (newUrl != null) { + trace("Redirecting to: " + newUrl); + result = downloadWithRedirects(newUrl, redirectCount + 1); + } + } + } + + http.onError = function(error:String) { + trace("HTTP Error: " + error); + } + + http.customRequest(false, output); + + if (result == null) { + result = output.getBytes(); + } + + return result; + } catch (e:Dynamic) { + trace("Error downloading update: " + e); + return null; + } + } + + private static function handleDownloadedData(data:Bytes):Void + { + try { + if (data == null || data.length == 0) { + throw "Downloaded data is empty"; + } + var tempPath = "temp_update.zip"; + trace("Downloading update, size: " + data.length + " bytes"); + File.saveBytes(tempPath, data); + trace("Update downloaded successfully"); + + if (!FileSystem.exists(tempPath) || FileSystem.stat(tempPath).size == 0) { + throw "Downloaded file is empty or doesn't exist"; + } + + extractUpdate(tempPath); + } catch (e:Dynamic) { + trace("Error saving update: " + e); + FlxG.state.add(new FlxText(0, 0, FlxG.width, "Update save failed: " + e)); + } + } + + private static function extractUpdate(zipPath:String):Void + { + try { + var zipFile = File.read(zipPath, true); + var entries = Reader.readZip(zipFile); + zipFile.close(); + + trace("Zip file opened, entries count: " + entries.length); + + for (entry in entries) { + var fileName = entry.fileName; + trace("Extracting: " + fileName); + + if (fileName == "AutoUpdater.exe" || fileName == "lime.ndll") { // replace with your executable's name. + var content = Reader.unzip(entry); + File.saveBytes(fileName + ".new", content); + trace("Saved new version of: " + fileName); + } else { + var content = Reader.unzip(entry); + var path = haxe.io.Path.directory(fileName); + if (path != "" && !FileSystem.exists(path)) { + FileSystem.createDirectory(path); + } + File.saveBytes(fileName, content); + trace("Extracted: " + fileName); + } + } + + FileSystem.deleteFile(zipPath); + trace("Temporary zip file deleted"); + finishUpdate(); + } catch (e:Dynamic) { + trace("Error during extraction: " + e); + FlxG.state.add(new FlxText(0, 0, FlxG.width, "Extraction failed: " + e)); + } + } + + private static function finishUpdate():Void + { + var batchContent = + '@echo off\n' + + 'timeout /t 1 /nobreak > NUL\n' + + 'move /y AutoUpdater.exe.new AutoUpdater.exe\n' + // replace with your executable's name. + 'move /y lime.ndll.new lime.ndll\n' + + 'start "" AutoUpdater.exe\n' + // replace with your executable's name. + 'del "%~f0"'; + + File.saveContent("finish_update.bat", batchContent); + + Sys.command("start finish_update.bat"); + Application.current.window.close(); + } +} \ No newline at end of file