moved packages (u need to update ur scripts!!!)

This commit is contained in:
Yoshman29
2023-04-12 02:00:34 +02:00
parent 42fc092a7e
commit f85d8b1675
76 changed files with 160 additions and 122 deletions
-168
View File
@@ -1,168 +0,0 @@
package funkin.system;
import funkin.mods.ModsAssetLibrary;
import lime.utils.AssetLibrary;
class AssetsLibraryList extends AssetLibrary {
public var libraries:Array<AssetLibrary> = [];
@:allow(funkin.system.Main)
@:allow(funkin.system.MainState)
private var __defaultLibraries:Array<AssetLibrary> = [];
public var base:AssetLibrary;
public function removeLibrary(lib:AssetLibrary) {
if (lib == null) return lib;
libraries.remove(lib);
return lib;
}
public function existsSpecific(id:String, type:String, source:AssetSource = BOTH) {
if (!id.startsWith("assets/") && exists('assets/$id', type))
return true;
for(k=>e in libraries) {
if (shouldSkipLib(k, source)) continue;
if (e.exists(id, type))
return true;
}
return false;
}
public override inline function exists(id:String, type:String):Bool
return existsSpecific(id, type, BOTH);
public function getSpecificPath(id:String, source:AssetSource = BOTH) {
for(k=>e in libraries) {
if (shouldSkipLib(k, source)) continue;
@:privateAccess
if (e.exists(id, e.types.get(id))) {
var path = e.getPath(id);
if (path != null)
return path;
}
}
return null;
}
public override inline function getPath(id:String)
return getSpecificPath(id, BOTH);
public function getFiles(folder:String, source:AssetSource = BOTH):Array<String> {
var content:Array<String> = [];
for(k=>e in libraries) {
if (shouldSkipLib(k, source)) continue;
var l = e;
if (l is openfl.utils.AssetLibrary) {
@:privateAccess
l = cast(l, openfl.utils.AssetLibrary).__proxy;
}
// TODO: do base folder scanning
#if MOD_SUPPORT
if (l is ModsAssetLibrary) {
var lib = cast(l, ModsAssetLibrary);
for(e in lib.getFiles(folder))
content.push(e);
}
#end
}
return content;
}
public function getFolders(folder:String, source:AssetSource = BOTH):Array<String> {
var content:Array<String> = [];
for(k=>e in libraries) {
if (shouldSkipLib(k, source)) continue;
var l = e;
if (l is openfl.utils.AssetLibrary) {
@:privateAccess
l = cast(l, openfl.utils.AssetLibrary).__proxy;
}
// TODO: do base folder scanning
#if MOD_SUPPORT
if (l is ModsAssetLibrary) {
var lib = cast(l, ModsAssetLibrary);
for(e in lib.getFolders(folder))
content.push(e);
}
#end
}
return content;
}
public function getSpecificAsset(id:String, type:String, source:AssetSource = BOTH):Dynamic {
try {
if (!id.startsWith("assets/")) {
var ass = getSpecificAsset('assets/$id', type, source);
if (ass != null) {
return ass;
}
}
for(k=>e in libraries) {
if (shouldSkipLib(k, source)) continue;
var asset = e.getAsset(id, type);
if (asset != null) {
return asset;
}
}
return null;
} catch(e) {
throw e;
}
return null;
}
private function shouldSkipLib(k:Int, source:AssetSource) {
return switch(source) {
case BOTH: false;
case SOURCE: k < libraries.length - __defaultLibraries.length;
case MODS: k >= libraries.length - __defaultLibraries.length;
};
}
public override inline function getAsset(id:String, type:String):Dynamic
return getSpecificAsset(id, type, BOTH);
public override function isLocal(id:String, type:String) {
return true;
}
public function new(?base:AssetLibrary) {
super();
if (base == null)
base = Assets.getLibrary("default");
addLibrary(this.base = base);
__defaultLibraries.push(base);
}
public function unloadLibraries() {
for(l in libraries)
if (!__defaultLibraries.contains(l))
l.unload();
}
public function reset() {
unloadLibraries();
libraries = [];
// adds default libraries in again
for(d in __defaultLibraries)
addLibrary(d);
}
public function addLibrary(lib:AssetLibrary) {
libraries.insert(0, lib);
return lib;
}
}
enum abstract AssetSource(Null<Bool>) from Bool from Null<Bool> to Null<Bool> {
var SOURCE = true;
var MODS = false;
var BOTH = null;
}
+1 -1
View File
@@ -37,7 +37,7 @@ class CommandLineHandler {
Sys.println("[ERROR] You need to specify the mod folder path");
Sys.exit(0);
} else if (FileSystem.exists(arg)) {
funkin.mods.ModsFolder.modsPath = arg;
funkin.assets.ModsFolder.modsPath = arg;
} else {
Sys.println('[ERROR] Mod folder at "${arg}" does not exist.');
Sys.exit(0);
+1 -1
View File
@@ -4,7 +4,7 @@ import funkin.chart.Chart;
import funkin.chart.ChartData;
import openfl.Lib;
import flixel.FlxState;
import funkin.interfaces.IBeatReceiver;
import funkin.system.interfaces.IBeatReceiver;
import flixel.util.FlxSignal.FlxTypedSignal;
/**
+2 -2
View File
@@ -4,11 +4,11 @@ import flixel.system.FlxAssets.FlxGraphicAsset;
import flixel.addons.effects.FlxSkewedSprite;
import haxe.io.Path;
import funkin.scripting.events.PlayAnimEvent.PlayAnimContext;
import funkin.interfaces.IOffsetCompatible;
import funkin.system.interfaces.IOffsetCompatible;
import flixel.math.FlxMatrix;
import flixel.math.FlxRect;
import flixel.math.FlxPoint;
import funkin.interfaces.IBeatReceiver;
import funkin.system.interfaces.IBeatReceiver;
@:enum
abstract XMLAnimType(Int)
+16
View File
@@ -0,0 +1,16 @@
package funkin.system;
import flixel.util.FlxColor;
import flixel.text.FlxText;
class FunkinText extends FlxText {
public function new(X:Float = 0, Y:Float = 0, FieldWidth:Float = 0, ?Text:String, Size:Int = 16, Border:Bool = true) {
super(X, Y, FieldWidth, Text, Size);
setFormat(Paths.font("vcr.ttf"), Size, FlxColor.WHITE);
if (Border) {
borderStyle = OUTLINE;
borderSize = 1;
borderColor = 0xFF000000;
}
}
}
+3 -2
View File
@@ -1,5 +1,6 @@
package funkin.system;
import funkin.assets.AssetsLibraryList;
import funkin.system.framerate.SystemInfo;
import openfl.utils.AssetLibrary;
import openfl.utils.AssetCache;
@@ -31,7 +32,7 @@ import sys.thread.Thread;
#if sys
import sys.io.File;
#end
import funkin.mods.ModsFolder;
import funkin.assets.ModsFolder;
class Main extends Sprite
{
@@ -116,7 +117,7 @@ class Main extends Sprite
Paths.assetsTree = new AssetsLibraryList();
#if UPDATE_CHECKING
funkin.updating.UpdateUtil.init();
funkin.system.updating.UpdateUtil.init();
#end
CrashHandler.init();
Logs.init();
+1 -1
View File
@@ -3,7 +3,7 @@ package funkin.system;
#if MOD_SUPPORT
import sys.FileSystem;
#end
import funkin.mods.ModsFolder;
import funkin.assets.ModsFolder;
import funkin.menus.TitleState;
import funkin.menus.BetaWarningState;
import flixel.FlxState;
+1 -1
View File
@@ -1,6 +1,6 @@
package funkin.system;
import funkin.interfaces.IBeatReceiver;
import funkin.system.interfaces.IBeatReceiver;
class MusicBeatGroup extends FlxTypedSpriteGroup<FlxSprite> implements IBeatReceiver {
public function beatHit(curBeat:Int) {
+1 -1
View File
@@ -7,7 +7,7 @@ import flixel.FlxState;
import flixel.FlxSubState;
import funkin.scripting.events.*;
import funkin.scripting.Script;
import funkin.interfaces.IBeatReceiver;
import funkin.system.interfaces.IBeatReceiver;
import funkin.system.Conductor;
import funkin.options.PlayerSettings;
+1 -1
View File
@@ -4,7 +4,7 @@ import flixel.FlxState;
import funkin.scripting.events.*;
import funkin.scripting.Script;
import funkin.scripting.DummyScript;
import funkin.interfaces.IBeatReceiver;
import funkin.system.interfaces.IBeatReceiver;
import funkin.system.Conductor.BPMChangeEvent;
import funkin.system.Conductor;
import funkin.options.PlayerSettings;
-294
View File
@@ -1,294 +0,0 @@
package funkin.system;
import flixel.graphics.frames.FlxFramesCollection;
import haxe.io.Path;
import funkin.mods.LimeLibrarySymbol;
import flixel.graphics.frames.FlxAtlasFrames;
import openfl.utils.AssetType;
import openfl.utils.Assets as OpenFlAssets;
import funkin.mods.ModsFolder;
import funkin.scripting.Script;
using StringTools;
class Paths
{
/**
* Preferred sound extension for the game's audio files.
* Currently is set to `mp3` for web targets, and `ogg` for other targets.
*/
inline public static var SOUND_EXT = #if web "mp3" #else "ogg" #end;
public static var assetsTree:AssetsLibraryList;
public static var tempFramesCache:Map<String, FlxFramesCollection> = [];
public static function init() {
FlxG.signals.preStateSwitch.add(function() {
tempFramesCache.clear();
});
}
public static function getPath(file:String, type:AssetType, library:Null<String>, skipModsVerification:Bool = false)
{
if (library != null)
return getLibraryPath(file, library);
return skipModsVerification ? 'assets:${getAssetsPath(file)}' : getAssetsPath(file);
}
static public function video(key:String) {
return getPath('videos/$key.mp4', BINARY, null);
}
static public function ndll(key:String) {
return getPath('ndlls/$key.ndll', BINARY, null);
}
static public function getLibraryPath(file:String, library = "default")
{
return if (library == "preload" || library == "default") getAssetsPath(file); else getLibraryPathForce(file, library);
}
inline static function getLibraryPathForce(file:String, library:String)
{
if (library.startsWith("mods")) library = library.toLowerCase();
return '$library:assets/$library/$file';
}
inline static function getAssetsPath(file:String)
{
return 'assets/$file';
}
inline static public function file(file:String, type:AssetType = TEXT, ?library:String)
{
return getPath(file, type, library);
}
inline static public function txt(key:String, ?library:String)
{
return getPath('data/$key.txt', TEXT, library);
}
inline static public function ini(key:String, ?library:String)
{
return getPath('data/$key.ini', TEXT, library);
}
inline static public function fragShader(key:String, ?library:String)
{
return getPath('shaders/$key.frag', TEXT, library);
}
inline static public function vertShader(key:String, ?library:String)
{
return getPath('shaders/$key.vert', TEXT, library);
}
inline static public function xml(key:String, ?library:String)
{
return getPath('data/$key.xml', TEXT, library);
}
inline static public function json(key:String, ?library:String)
{
return getPath('data/$key.json', TEXT, library);
}
inline static public function ps1(key:String, ?library:String)
{
return getPath('data/$key.ps1', TEXT, library);
}
static public function sound(key:String, ?library:String)
{
return getPath('sounds/$key.$SOUND_EXT', SOUND, library);
}
inline static public function soundRandom(key:String, min:Int, max:Int, ?library:String)
{
return sound(key + FlxG.random.int(min, max), library);
}
inline static public function music(key:String, ?library:String)
{
return getPath('music/$key.$SOUND_EXT', MUSIC, library);
}
inline static public function voices(song:String, difficulty:String = "normal")
{
var diff = getPath('songs/${song.toLowerCase()}/song/Voices-${difficulty.toLowerCase()}.$SOUND_EXT', MUSIC, null);
return OpenFlAssets.exists(diff) ? diff : getPath('songs/${song.toLowerCase()}/song/Voices.$SOUND_EXT', MUSIC, null);
}
inline static public function inst(song:String, difficulty:String = "normal")
{
var diff = getPath('songs/${song.toLowerCase()}/song/Inst-${difficulty.toLowerCase()}.$SOUND_EXT', MUSIC, null);
return OpenFlAssets.exists(diff) ? diff : getPath('songs/${song.toLowerCase()}/song/Inst.$SOUND_EXT', MUSIC, null);
}
inline static public function image(key:String, ?library:String, checkForAtlas:Bool = false)
{
var atlasPath = checkForAtlas ? getPath('images/$key/spritemap.png', IMAGE, library) : null;
var multiplePath = checkForAtlas ? getPath('images/$key/1.png', IMAGE, library) : null;
if (atlasPath != null && OpenFlAssets.exists(atlasPath)) return atlasPath.substr(0, atlasPath.length - 14);
if (multiplePath != null && OpenFlAssets.exists(multiplePath)) return multiplePath.substr(0, multiplePath.length - 6);
return getPath('images/$key.png', IMAGE, library);
}
inline static public function script(key:String, ?library:String, isAssetsPath:Bool = false) {
var scriptPath = isAssetsPath ? key : getPath(key, TEXT, library);
var p:String;
for(ex in Script.scriptExtensions) {
p = isAssetsPath ? '$key.$ex' : getPath('$key.$ex', TEXT, library);
if (OpenFlAssets.exists(p)) {
scriptPath = p;
break;
}
}
return scriptPath;
}
static public function chart(song:String, ?difficulty:String = "normal"):String {
difficulty = difficulty.toLowerCase();
song = song.toLowerCase();
//var difficultyEnd = (difficulty == "normal") ? "" : '-$difficulty';
// songs/your-song/charts/hard.json
var p = getPath('songs/$song/charts/$difficulty.json', TEXT, null);
if (OpenFlAssets.exists(p)) return p;
// data/charts/your-song/hard.json
var p = json('charts/$song/$difficulty');
if (OpenFlAssets.exists(p)) return p;
return p; // returns the normal one so that it shows the correct path in the error message.
}
inline static public function font(key:String)
{
return getPath('fonts/$key', FONT, null);
}
inline static public function obj(key:String) {
return getPath('models/$key.obj', BINARY, null);
}
inline static public function dae(key:String) {
return getPath('models/$key.dae', BINARY, null);
}
inline static public function md2(key:String) {
return getPath('models/$key.md2', BINARY, null);
}
inline static public function md5(key:String) {
return getPath('models/$key.md5', BINARY, null);
}
inline static public function awd(key:String) {
return getPath('models/$key.awd', BINARY, null);
}
inline static public function getSparrowAtlas(key:String, ?library:String)
{
return FlxAtlasFrames.fromSparrow(image(key, library), file('images/$key.xml', library));
}
static public function getFrames(key:String, ?library:String) {
if (tempFramesCache.exists(key)) {
if (tempFramesCache[key].parent.bitmap.readable)
return tempFramesCache[key];
else
tempFramesCache[key] = null;
}
return tempFramesCache[key] = CoolUtil.loadFrames(Paths.image(key, library, true));
}
inline static public function getSparrowAtlasAlt(key:String)
{
return FlxAtlasFrames.fromSparrow('$key.png', '$key.xml');
}
inline static public function getPackerAtlas(key:String, ?library:String)
{
return FlxAtlasFrames.fromSpriteSheetPacker(image(key, library), file('images/$key.txt', library));
}
inline static public function getPackerAtlasAlt(key:String)
{
return FlxAtlasFrames.fromSpriteSheetPacker('$key.png', '$key.txt');
}
static public function getFolderDirectories(key:String, addPath:Bool = false, source:funkin.system.AssetsLibraryList.AssetSource = BOTH):Array<String> {
if (!key.endsWith("/")) key += "/";
var content = assetsTree.getFolders('assets/$key', source);
if (addPath) {
for(k=>e in content)
content[k] = '$key$e';
}
return content;
}
static public function getFolderContent(key:String, addPath:Bool = false, source:funkin.system.AssetsLibraryList.AssetSource = BOTH):Array<String> {
// designed to work both on windows and web
if (!key.endsWith("/")) key += "/";
var content = assetsTree.getFiles('assets/$key', source);
if (addPath) {
for(k=>e in content)
content[k] = '$key$e';
}
return content;
/*
if (!key.endsWith("/")) key = key + "/";
if (ModsFolder.currentModFolder == null && !scanSource)
return getFolderContent(key, false, addPath, true);
var folderPath:String = scanSource ? getAssetsPath(key) : getLibraryPathForce(key, 'mods/${ModsFolder.currentModFolder}');
var libThing = new LimeLibrarySymbol(folderPath);
var library = libThing.library;
if (library is openfl.utils.AssetLibrary) {
var lib = cast(libThing.library, openfl.utils.AssetLibrary);
@:privateAccess
if (lib.__proxy != null) library = lib.__proxy;
}
var content:Array<String> = [];
#if MOD_SUPPORT
if (library is funkin.mods.ModsAssetLibrary) {
// easy task, can immediately scan for files!
var lib = cast(library, funkin.mods.ModsAssetLibrary);
content = lib.getFiles(libThing.symbolName);
if (addPath)
for(i in 0...content.length)
content[i] = '$folderPath${content[i]}';
} else #end {
@:privateAccess
for(k=>e in library.paths) {
if (k.toLowerCase().startsWith(libThing.symbolName.toLowerCase())) {
if (addPath) {
if (libThing.libraryName != "")
content.push('${libThing.libraryName}:$k');
else
content.push(k);
} else {
var barebonesFileName = k.substr(libThing.symbolName.length);
if (!barebonesFileName.contains("/"))
content.push(barebonesFileName);
}
}
}
}
if (includeSource) {
var sourceResult = getFolderContent(key, false, addPath, true);
for(e in sourceResult)
if (!content.contains(e))
content.push(e);
}
return content;
*/
}
}
@@ -1,6 +1,6 @@
package funkin.system.framerate;
import funkin.mods.ModsAssetLibrary;
import funkin.assets.ModsAssetLibrary;
class AssetTreeInfo extends FramerateCategory {
public function new() {
@@ -3,7 +3,7 @@ package funkin.system.framerate;
import openfl.text.TextFormat;
import openfl.display.Sprite;
import openfl.text.TextField;
import funkin.macros.GitCommitMacro;
import funkin.system.macros.GitCommitMacro;
class CodenameBuildField extends TextField {
public function new() {
+134
View File
@@ -0,0 +1,134 @@
package funkin.system.github;
#if GITHUB_API
import haxe.Json;
import haxe.Exception;
import haxe.Http;
// TODO: Document further and perhaps make this a Haxelib.
class GitHub {
/**
* Gets all the releases from a specific GitHub repository using the GitHub API.
* @param user
* @param repository
* @return Releases
*/
public static function getReleases(user:String, repository:String, ?onError:Exception->Void):Array<GitHubRelease> {
try {
var url = 'https://api.github.com/repos/${user}/${repository}/releases';
var data = Json.parse(__requestOnGitHubServers(url));
if (!(data is Array))
throw __parseGitHubException(data);
return data;
} catch(e) {
if (onError != null)
onError(e);
}
return [];
}
public static function getContributors(user:String, repository:String, ?onError:Exception->Void):Array<GitHubContributor> {
try {
var url = 'https://api.github.com/repos/${user}/${repository}/contributors';
var data = Json.parse(__requestOnGitHubServers(url));
if (!(data is Array))
throw __parseGitHubException(data);
return data;
} catch(e) {
if (onError != null)
onError(e);
}
return [];
}
/**
* Filters all releases gotten by `getReleases`
* @param releases Releases
* @param keepPrereleases Whenever to keep Pre-Releases.
* @param keepDrafts Whenever to keep Drafts.
* @return Filtered releases.
*/
public static inline function filterReleases(releases:Array<GitHubRelease>, keepPrereleases:Bool = true, keepDrafts:Bool = false)
return [for(release in releases) if (release != null && (!release.prerelease || (release.prerelease && keepPrereleases)) && (!release.draft || (release.draft && keepDrafts))) release];
public static function __requestOnGitHubServers(url:String) {
var h = new Http(url);
h.setHeader("User-Agent", "request");
var r = null;
h.onData = function(d) {
r = d;
}
h.onError = function(e) {
throw e;
}
h.request(false);
return r;
}
public static function __requestBytesOnGitHubServers(url:String) {
var h = new Http(url);
h.setHeader("User-Agent", "request");
var r = null;
h.onBytes = function(d) {
r = d;
}
h.onError = function(e) {
throw e;
}
h.request(false);
return r;
}
private static function __parseGitHubException(obj:Dynamic):GitHubException {
var msg:String = "(No message)";
var url:String = "(No API url)";
if (Reflect.hasField(obj, "message"))
msg = Reflect.field(obj, "message");
if (Reflect.hasField(obj, "documentation_url"))
url = Reflect.field(obj, "documentation_url");
return new GitHubException(msg, url);
}
}
#else
import haxe.Json;
import haxe.Exception;
class GitHub {
/**
* Gets all the releases from a specific GitHub repository using the GitHub API.
* @param user
* @param repository
* @return Releases
*/
public static function getReleases(user:String, repository:String, ?onError:Exception->Void):Array<GitHubRelease> {
return [];
}
public static function getContributors(user:String, repository:String, ?onError:Exception->Void):Array<GitHubContributor> {
return [];
}
/**
* Filters all releases gotten by `getReleases`
* @param releases Releases
* @param keepPrereleases Whenever to keep Pre-Releases.
* @param keepDrafts Whenever to keep Drafts.
* @return Filtered releases.
*/
public static inline function filterReleases(releases:Array<GitHubRelease>, keepPrereleases:Bool = true, keepDrafts:Bool = false)
return releases;
public static function __requestOnGitHubServers(url:String) {
return null;
}
public static function __requestBytesOnGitHubServers(url:String) {
return null;
}
private static function __parseGitHubException(obj:Dynamic):GitHubException {
return null;
}
}
#end
@@ -0,0 +1,19 @@
package funkin.system.github;
import funkin.system.github.GitHubUser;
typedef GitHubAsset = {
var url:String;
var id:Int;
var node_id:String;
var name:String;
var label:String;
var uploader:GitHubUser;
var content_type:String;
var state:String;
var size:UInt;
var download_count:Int;
var created_at:String;
var updated_at:String;
var browser_download_url:String;
}
@@ -0,0 +1,23 @@
package funkin.system.github;
typedef GitHubContributor = {
var login:String;
var id:Int;
var node_id:String;
var avatar_url:String;
var gravatar_id:String;
var url:String;
var html_url:String;
var followers_url:String;
var following_url:String;
var gists_url:String;
var starred_url:String;
var subscriptions_url:String;
var organizations_url:String;
var repos_url:String;
var events_url:String;
var received_events_url:String;
var type:String;
var site_admin:Bool;
var contributions:Int;
}
@@ -0,0 +1,15 @@
package funkin.system.github;
import haxe.Exception;
class GitHubException extends Exception {
public var apiMessage:String;
public var documentationUrl:String;
public function new(apiMessage:String, documentationUrl:String) {
super('[GitHubException] ${apiMessage} (Check ${documentationUrl})');
this.apiMessage = apiMessage;
this.documentationUrl = documentationUrl;
}
}
@@ -0,0 +1,13 @@
package funkin.system.github;
typedef GitHubReactions = {
var url:String;
var total_count:Int;
// +1 and -1 cant be added, you need to use Reflect.field
var laugh:Int;
var hooray:Int;
var confused:Int;
var heart:Int;
var rocket:Int;
var eyes:Int;
}
@@ -0,0 +1,62 @@
package funkin.system.github;
typedef GitHubRelease = {
/**
* Body of the GitHub request (Markdown)
*/
var body:String;
/**
* Url of the release (GitHub API)
*/
var url:String;
/**
* Url for the assets JSON. Also accessible via `GitHubRelease.assets`
*/
var assets_url:String;
/**
* Template URL for asset download link.
*/
var upload_url:String;
/**
* Link to the release on the GitHub website.
*/
var html_url:String;
/**
* ID of the release.
*/
var id:Int;
/**
* Author of the release
*/
var author:GitHubUser;
var node_id:String;
var tag_name:String;
var target_commitish:String;
var name:String;
var draft:Bool;
var prerelease:Bool;
var created_at:String;
var published_at:String;
var assets:Array<GitHubAsset>;
var tarball_url:String;
var zipball_url:String;
var reactions:GitHubReactions;
}
+132
View File
@@ -0,0 +1,132 @@
package funkin.system.github;
typedef GitHubUser = {
/**
* Username of the user.
*/
var login:String;
/**
* ID of the user.
*/
var id:Int;
/**
* ID of the current node on the GitHub database.
*/
var node_id:String;
/**
* Link to the avatar (profile picture).
*/
var avatar_url:String;
/**
* Unknown
*/
var gravatar_id:String;
/**
* URL to the user on GitHub's servers.
*/
var url:String;
/**
* URL to the user on GitHub's website.
*/
var html_url:String;
/**
* URL on GitHub's API to access this user's followers.
*/
var followers_url:String;
/**
* URL on GitHub's API to access the accounts this user is following.
*/
var following_url:String;
/**
* URL on GitHub's API to access this user's gists.
*/
var gists_url:String;
/**
* URL on GitHub's API to access this user's starred repositories.
*/
var starred_url:String;
// NOT COMPLETE: MISSING repos_url, organizations_url, subscriptions_url, events_url, received_events_url.
/**
* Type of the user.
*/
var type:GitHubUserType;
/**
* Whenever the user is a GitHub administrator.
*/
var site_admin:Bool;
/**
* Name of the user.
*/
var name:String;
/**
* The company this user belongs to. Can be `null`.
*/
var company:String;
var blog:String;
var location:String;
var email:String;
var hireable:Null<Bool>;
var bio:String;
/**
* Twitter username of the user. Can be null.
*/
var twitter_username:String;
/**
* Number of public repos this user own.
*/
var public_repos:Int;
/**
* Number of public gists this user own.
*/
var public_gists:Int;
/**
* Number of followers this user have
*/
var followers:Int;
/**
* Number of accounts this user follows.
*/
var following:Int;
/**
* Date of creation of the account
*/
var created_at:String;
/**
* Date of last account update.
*/
var updated_at:String;
}
@:enum
abstract GitHubUserType(String) {
var USER = "User";
var ORGANIZATION = "Organization";
}
@@ -0,0 +1,7 @@
package funkin.system.interfaces;
interface IBeatReceiver {
public function measureHit(curMeasure:Int):Void;
public function beatHit(curBeat:Int):Void;
public function stepHit(curStep:Int):Void;
}
@@ -0,0 +1,5 @@
package funkin.system.interfaces;
interface IOffsetCompatible {
public function addOffset(anim:String, x:Float = 0, y:Float = 0):Void;
}
@@ -0,0 +1,32 @@
package funkin.system.macros;
#if macro
import sys.io.Process;
import haxe.macro.Context;
import haxe.macro.Compiler;
import haxe.macro.Expr;
#end
class DefinesMacro {
/**
* Returns the defined values
*/
public static var defines(get, null):Map<String, Dynamic>;
// GETTERS
#if REGION
private static inline function get_defines()
return __getDefines();
#end
// INTERNAL MACROS
#if REGION
private static macro function __getDefines() {
#if display
return macro $v{[]};
#else
return macro $v{Context.getDefines()};
#end
}
#end
}
+121
View File
@@ -0,0 +1,121 @@
package funkin.system.macros;
#if macro
/**
* Macro that generates all additional fields, making events much easier to code in.
* It adds the `recycle` function, which allows you to "reset" an event's values.
*/
class EventMacro {
public static function build():Array<Field> {
var fields = Context.getBuildFields();
var curClassRest = Context.getLocalClass();
if (curClassRest == null) return fields;
var curClass = curClassRest.get();
if (curClass == null || curClass.name == "CancellableEvent") return fields;
for(f in fields)
if (f.name == "recycle")
return fields;
// gets all fields
var values:Array<EventVar> = [];
var hiddenValues:Array<EventVar> = [];
for(field in fields) {
if (field.access.contains(AStatic)) continue;
var hidden = false;
if (field.meta != null)
for(m in field.meta)
if (m.name == ":dox")
hidden = true;
if (!field.access.contains(APublic)) hidden = true;
switch(field.kind) {
case FVar(type, expr):
(hidden ? hiddenValues : values).push({
name: field.name,
type: type,
expr: expr
});
default:
continue;
}
}
// add recycle option
var func:Function = {
args: [for(a in values) {
value: a.expr,
type: a.type,
opt: false,
name: a.name
}],
expr: {
pos: Context.currentPos(),
expr: EBlock([])
}
};
var funcField:Field = {
pos: Context.currentPos(),
name: "recycle",
kind: FFun(func),
access: [APublic]
};
fields.push(funcField);
switch(func.expr.expr) {
case EBlock(exprs):
// add a "set this" expr for each variable
for(v in values) {
var name = v.name;
exprs.push({
pos: Context.currentPos(),
expr: EBinop(OpAssign, {
pos: Context.currentPos(),
expr: EField({
pos: Context.currentPos(),
expr: EConst(CIdent("this"))
}, name)
}, {
pos: Context.currentPos(),
expr: EConst(CIdent(name))
})
});
}
// add a "set this" expr to reset each private/hidden variables
for(v in hiddenValues) {
var name = v.name;
exprs.push({
pos: Context.currentPos(),
expr: EBinop(OpAssign, {
pos: Context.currentPos(),
expr: EField({
pos: Context.currentPos(),
expr: EConst(CIdent("this"))
}, name)
}, v.expr)
});
}
exprs.push(macro return this);
exprs.insert(0, macro recycleBase());
default:
// nothing
}
return fields;
}
}
typedef EventVar = {
var name:String;
var type:ComplexType;
var expr:Expr;
}
#end
@@ -0,0 +1,101 @@
package funkin.system.macros;
#if macro
/**
* Macro that automatically generates flush and load functions.
*/
class FunkinSaveMacro {
/**
* Generates flush and load functions.
* @param saveFieldName Name of the save field (`save`)
* @param saveFuncName Name of the save func (`flush`)
* @param loadFuncName Name of the load func (`load`)
* @return Array<Field>
*/
public static function build(saveFieldName:String = "save", saveFuncName:String = "flush", loadFuncName:String = "load"):Array<Field> {
var fields:Array<Field> = Context.getBuildFields();
var fieldNames:Array<String> = [];
for(field in fields) {
if (!field.access.contains(AStatic)) continue;
switch(field.kind) {
case FVar(type, expr):
if (field.name == saveFieldName) continue;
var valid:Bool = true;
if (field.meta != null)
for(m in field.meta)
if (m.name == ":doNotSave") {
valid = false;
break;
}
if (valid)
fieldNames.push(field.name);
default:
continue;
}
}
/**
* SAVE FUNCTION
*/
var saveFuncBlocks:Array<Expr> = [for(f in fieldNames)
macro ${{
pos: Context.currentPos(),
expr: EConst(CIdent(saveFieldName))
}}.data.$f = ${{
pos: Context.currentPos(),
expr: EConst(CIdent(f))
}}
];
saveFuncBlocks.push(macro ${{
pos: Context.currentPos(),
expr: EConst(CIdent(saveFieldName))
}}.flush());
fields.push({
pos: Context.currentPos(),
name: saveFuncName,
kind: FFun({
args: [],
expr: {
pos: Context.currentPos(),
expr: EBlock(saveFuncBlocks)
}
}),
access: [APublic, AStatic]
});
/**
* LOAD FUNCTION
*/
fields.push({
pos: Context.currentPos(),
name: loadFuncName,
kind: FFun({
args: [],
expr: {
pos: Context.currentPos(),
expr: EBlock([for(f in fieldNames)
macro if (${{
pos: Context.currentPos(),
expr: EConst(CIdent(saveFieldName))
}}.data.$f != null) ${{
pos: Context.currentPos(),
expr: EConst(CIdent(f))
}} = ${{
pos: Context.currentPos(),
expr: EConst(CIdent(saveFieldName))
}}.data.$f
])
}
}),
access: [APublic, AStatic]
});
return fields;
}
}
#end
@@ -0,0 +1,56 @@
package funkin.system.macros;
#if macro
import sys.io.Process;
#end
class GitCommitMacro {
/**
* Returns the current commit number
*/
public static var commitNumber(get, null):Int;
/**
* Returns the current commit hash
*/
public static var commitHash(get, null):String;
// GETTERS
#if REGION
private static inline function get_commitNumber()
return __getCommitNumber();
private static inline function get_commitHash()
return __getCommitHash();
#end
// INTERNAL MACROS
#if REGION
private static macro function __getCommitHash() {
#if display
return macro $v{"-"};
#else
try {
var proc = new Process('git', ['rev-parse', '--short', 'HEAD'], false);
proc.exitCode(true);
return macro $v{proc.stdout.readLine()};
} catch(e) {}
return macro $v{"-"}
#end
}
private static macro function __getCommitNumber() {
#if display
return macro $v{0};
#else
try {
var proc = new Process('git', ['rev-list', 'HEAD', '--count'], false);
proc.exitCode(true);
return macro $v{Std.parseInt(proc.stdout.readLine())};
} catch(e) {}
return macro $v{0}
#end
}
#end
}
@@ -0,0 +1,54 @@
package funkin.system.macros;
#if macro
using StringTools;
/**
* Macro used in the options class to add getters for the SOLO keybinds.
*/
class OptionsMacro {
public static function build():Array<Field> {
var fields = Context.getBuildFields();
for(f in fields) {
switch(f.kind) {
case FProp(get, set, t, e):
if (f.name.startsWith('SOLO_') && get == "get" && set == "null") {
// Auto-create getters for solo (P1/P2) keybinds
var name = f.name.substr(5);
var getterName = 'get_SOLO_$name';
var ponename = 'P1_$name';
var ptwoname = 'P2_$name';
var newFuncExpr:Expr = macro {
var a:Array<FlxKey> = [];
for(e in ${{
pos: Context.currentPos(),
expr: EConst(CIdent(ponename))
}}) a.push(e);
for(e in ${{
pos: Context.currentPos(),
expr: EConst(CIdent(ptwoname))
}}) a.push(e);
return a;
};
fields.push({
name: getterName,
kind: FFun({
expr: newFuncExpr,
args: []
}),
pos: Context.currentPos(),
access: [APrivate, AStatic, AInline]
});
}
default:
// nothing
}
}
return fields;
}
}
#end
@@ -0,0 +1,34 @@
package funkin.system.macros;
#if macro
import haxe.macro.Context;
import haxe.macro.Compiler;
import haxe.macro.Expr;
/**
* Macros containing additional help functions to expand HScript capabilities.
*/
class ScriptsMacro {
public static function addAdditionalClasses() {
for(inc in [
// FLIXEL
"flixel.util", "flixel.ui", "flixel.tweens", "flixel.tile", "flixel.text",
"flixel.system", "flixel.sound", "flixel.path", "flixel.math", "flixel.input",
"flixel.group", "flixel.graphics", "flixel.effects", "flixel.animation",
// FLIXEL ADDONS
"flixel.addons.api", "flixel.addons.display", "flixel.addons.effects", "flixel.addons.ui",
"flixel.addons.plugin", "flixel.addons.text", "flixel.addons.tile", "flixel.addons.transition",
"flixel.addons.util",
// OTHER LIBRARIES & STUFF
"away3d", "flx3d", #if sys "sys", #end
// BASE HAXE
"DateTools", "EReg", "Lambda", "StringBuf", "haxe.crypto", "haxe.display", "haxe.exceptions", "haxe.extern", "scripting"
])
Compiler.include(inc);
// FOR ABSTRACTS
Compiler.addGlobalMetadata('haxe.xml', '@:build(hscript.UsingHandler.build())');
Compiler.addGlobalMetadata('haxe.CallStack', '@:build(hscript.UsingHandler.build())');
}
}
#end
@@ -0,0 +1,246 @@
package funkin.system.macros;
using StringTools;
class SoftcodedClassMacro {
#if macro
private static var nonAllowedSuffixes:Array<String> = ["__Softcoded", "_HSC", "_HSX", "_Impl_"];
public static function init() {
Compiler.addGlobalMetadata('funkin.game.cutscenes', '@:build(funkin.system.macros.SoftcodedClassMacro.build())');
Compiler.addGlobalMetadata('funkin.game', '@:build(funkin.system.macros.SoftcodedClassMacro.build())');
Compiler.addGlobalMetadata('funkin.menus', '@:build(funkin.system.macros.SoftcodedClassMacro.build())');
Compiler.addGlobalMetadata('funkin.options', '@:build(funkin.system.macros.SoftcodedClassMacro.build())');
Compiler.addGlobalMetadata('funkin.scripting', '@:build(funkin.system.macros.SoftcodedClassMacro.build())');
Compiler.addGlobalMetadata('funkin.shaders', '@:build(funkin.system.macros.SoftcodedClassMacro.build())');
Compiler.addGlobalMetadata('flixel', '@:build(funkin.system.macros.SoftcodedClassMacro.build())', false);
}
public static function build():Array<Field> {
var fields = Context.getBuildFields();
var cl = Context.getLocalClass();
if (cl != null) {
var scriptClassType = TPath({
pack: ['funkin', 'scripting'],
name: 'Script',
sub: 'ScriptClass'
});
var funcs:Array<Field> = [];
funcs.push({
pos: Context.currentPos(),
name: '__scriptClass',
kind: FVar(scriptClassType, macro null)
});
var c = cl.get();
c.isPrivate = false;
switch(c.kind) {
case KNormal:
// do nothing
default:
return fields;
}
if (c.isPrivate || c.isInterface || c.isFinal || c.isExtern || c.isAbstract) return fields;
for(s in nonAllowedSuffixes)
if (c.name.endsWith(s))
return fields;
if (c.params.length > 0) return fields; // NOT SUPPORTED
if (c.isAbstract) return fields;
for(m in c.meta.get()) {
switch(m.name) {
case ":final", ":noSoftcodedExtend":
return fields;
default:
// cool
}
}
var shadowClass = macro class {
};
// var fixedPack = [for(p in c.pack) if (p.startsWith("_")) p.substr(1) else p];
shadowClass.name = '${c.name}__Softcoded';
shadowClass.fields = funcs;
shadowClass.pack = c.pack.copy();
shadowClass.kind = TDClass({
pack: c.pack.copy(),
name: c.name
}, [], false, false, false);
var addedFields:Array<String> = [];
#if !display
for(f in [for(f in fields) f]) {
var valid = true;
if (f.access != null) for(e in f.access) {
switch (e) {
case AStatic | ADynamic | AInline | AMacro | AFinal | AExtern | AAbstract:
valid = false;
break;
default:
// nothing
}
}
if (!valid) continue;
switch(f.kind) {
case FFun(fun):
if (fun.params != null && fun.params.length > 0) continue;
var funcExpr = macro {
${{
pos: Context.currentPos(),
expr: ECall({
pos: Context.currentPos(),
expr: EField({
pos: Context.currentPos(),
expr: EConst(CIdent("super"))
}, f.name)
}, fun.args != null ? [for(a in fun.args) {
pos: Context.currentPos(),
expr: EConst(CIdent(a.name))
}] : [])
}};
};
var isVoid = fun.ret.match(TPath({name: "Void"}));
if (!isVoid)
funcExpr = macro return ${funcExpr};
var accessors = [];
if (f.access != null) for(f in f.access) accessors.push(f);
accessors.remove(AOverride);
if (f.name == "new") {
var constructorExpr = macro {
${{
expr: ECall({
pos: Context.currentPos(),
expr: EConst(CIdent("super"))
}, fun.args != null ? [for(a in fun.args) {
pos: Context.currentPos(),
expr: EConst(CIdent(a.name))
}] : []),
pos: Context.currentPos()
}}
__scriptClass = scrObj;
__scriptClass.call("new", ${{
pos: Context.currentPos(),
expr: EArrayDecl([for(a in fun.args) {
pos: Context.currentPos(),
expr: EConst(CIdent(a.name))
}])
}}, this);
}
var func:Function = {
expr: constructorExpr,
args: [for(a in fun.args) a]
};
func.args.insert(0, {
name: "scrObj",
type: scriptClassType
});
funcs.push({
pos: Context.currentPos(),
name: 'new',
kind: FFun(func),
access: [for(a in accessors) a]
});
var printer = new Printer();
continue;
} else {
var func:Function = {
expr: funcExpr,
args: fun.args
};
funcs.push({
pos: Context.currentPos(),
name: '__super__${f.name}',
kind: FFun(func),
access: [for(a in accessors) a]
});
}
accessors.push(AOverride);
var overrideFuncExpr = isVoid ? macro {
var fieldName = ${{
pos: Context.currentPos(),
expr: EConst(CString(f.name, DoubleQuotes))
}};
if (__scriptClass != null && __scriptClass.hasField(fieldName) && Reflect.isFunction(__scriptClass.get(fieldName))) {
__scriptClass.call(fieldName, ${{
pos: Context.currentPos(),
expr: EArrayDecl([for(a in fun.args) {
pos: Context.currentPos(),
expr: EConst(CIdent(a.name))
}])
}}, this);
} else ${funcExpr}
} : macro {
var fieldName = ${{
pos: Context.currentPos(),
expr: EConst(CString(f.name, DoubleQuotes))
}};
if (__scriptClass != null && __scriptClass.hasField(fieldName) && Reflect.isFunction(__scriptClass.get(fieldName))) {
return __scriptClass.call(fieldName, ${{
pos: Context.currentPos(),
expr: EArrayDecl([for(a in fun.args) {
pos: Context.currentPos(),
expr: EConst(CIdent(a.name))
}])
}}, this);
} else ${funcExpr}
};
var overrideFunc:Function = {
expr: overrideFuncExpr,
args: fun.args
};
funcs.push({
pos: Context.currentPos(),
name: '${f.name}',
kind: FFun(overrideFunc),
access: accessors
});
default:
}
}
#end
var imports = [for(i in Context.getLocalImports()) i];
imports.push({
path: [for(p in c.module.split(".")) {
pos: Context.currentPos(),
name: p
}],
mode: IAll
});
imports.push({
path: [for(p in c.module.split(".")) {
pos: Context.currentPos(),
name: p
}],
mode: INormal
});
Context.defineModule(c.module + "__Softcoded", [shadowClass], imports);
}
return fields;
}
#end
}
+5
View File
@@ -0,0 +1,5 @@
#if macro
import haxe.macro.*;
import haxe.macro.Expr;
import haxe.macro.Type;
#end
@@ -0,0 +1,194 @@
package funkin.system.updating;
import openfl.Lib;
import sys.io.Process;
import haxe.zip.Reader;
import funkin.utils.ZipUtil;
import haxe.io.Path;
import openfl.utils.ByteArray;
import sys.io.File;
import sys.io.FileOutput;
import openfl.events.Event;
import openfl.events.ProgressEvent;
import openfl.net.URLRequest;
import openfl.net.URLLoader;
import sys.FileSystem;
import funkin.system.github.GitHubRelease;
class AsyncUpdater {
// NON ASYNC STUFF
#if REGION
public function new(releases:Array<GitHubRelease>) {
this.releases = releases;
}
public function execute() {
Main.execAsync(installUpdates);
}
#end
#if windows
public static var executableGitHubName:String = "update-windows.exe";
public static var executableName:String = "CodenameEngine.exe";
#end
#if linux
public static var executableGitHubName:String = "update-linux";
public static var executableName:String = "CodenameEngine";
#end
#if mac
public static var executableGitHubName:String = "update-mac";
public static var executableName:String = "CodenameEngine";
#end
public var releases:Array<GitHubRelease>;
public var progress:UpdaterProgress = new UpdaterProgress();
public var path:String;
public var downloadStream:URLLoader;
public var executableReplaced:Bool = false;
public var lastTime:Float = 0;
public var oldBytesLoaded:Float = 0;
public function installUpdates() {
prepareInstallationEnvironment();
downloadFiles();
}
public function installFiles(files:Array<String>) {
progress.step = INSTALLING;
progress.files = files.length+1;
for(k=>e in files) {
var path = '$path$e';
progress.curFile = k+1;
progress.curFileName = e;
trace('extracting file ${path}');
var reader = ZipUtil.openZip(path);
progress.curZipProgress = new ZipProgress();
ZipUtil.uncompressZip(reader, './', null, progress.curZipProgress);
// FileSystem.deleteFile(path);
}
if (executableReplaced = FileSystem.exists('$path$executableName')) {
progress.curFile = files.length;
progress.curFileName = executableName;
var progPath = Sys.programPath();
var bakFile = '${Path.withoutExtension(progPath)}.bak';
if (FileSystem.exists(bakFile))
FileSystem.deleteFile(bakFile);
FileSystem.rename(progPath, bakFile);
FileSystem.rename('$path$executableName', progPath);
}
}
public function downloadFiles() {
var files:Array<String> = [];
var fileNames:Array<String> = [];
var exePath:String = "";
for(r in releases) {
for(e in r.assets) {
if (e.name.toLowerCase() == "update-assets.zip") {
files.push(e.browser_download_url);
fileNames.push('${Path.withoutExtension(e.name)}-${r.tag_name}.${Path.extension(e.name)}');
} else if (e.name.toLowerCase() == executableGitHubName) {
exePath = e.browser_download_url;
}
}
}
progress.curFile = -1;
progress.curFileName = null;
progress.files = files.length;
progress.step = DOWNLOADING_ASSETS;
trace('starting assets download');
doFile([for(e in files) e], [for(e in fileNames) e], function() {
progress.curFile = -1;
progress.curFileName = null;
progress.files = 1;
progress.step = DOWNLOADING_EXECUTABLE;
trace('starting exe download');
doFile([exePath], [executableName], function() {
trace('done, starting installation');
installFiles(fileNames);
progress.done = true;
});
});
}
public function doFile(files:Array<String>, fileNames:Array<String>, onFinish:Void->Void) {
var f = files.shift();
if (f == null) {
onFinish();
return;
}
var fn = fileNames.shift();
trace('downloading $f ($fn)');
progress.curFile++;
progress.curFileName = fn;
progress.bytesLoaded = 0;
progress.bytesTotal = 1;
downloadStream = new URLLoader();
downloadStream.dataFormat = BINARY;
downloadStream.addEventListener(ProgressEvent.PROGRESS, function(e) {
progress.bytesLoaded = e.bytesLoaded;
progress.bytesTotal = e.bytesTotal;
var curTime = Lib.getTimer();
progress.downloadSpeed = (e.bytesLoaded - oldBytesLoaded) / ((curTime - lastTime) / 1000);
lastTime = curTime;
oldBytesLoaded = e.bytesLoaded;
});
downloadStream.addEventListener(Event.COMPLETE, function(e) {
var fileOutput:FileOutput = File.write('$path$fn', true);
var data:ByteArray = new ByteArray();
downloadStream.data.readBytes(data, 0, downloadStream.data.length - downloadStream.data.position);
fileOutput.writeBytes(data, 0, data.length);
fileOutput.flush();
fileOutput.close();
doFile(files, fileNames, onFinish);
});
oldBytesLoaded = 0;
lastTime = Lib.getTimer();
downloadStream.load(new URLRequest(f));
}
public function prepareInstallationEnvironment() {
progress.step = PREPARING;
#if windows
path = '${Sys.getEnv("TEMP")}\\Codename Engine\\Updater\\';
#else
path = '.temp/';
#end
FileSystem.createDirectory(path);
}
}
class UpdaterProgress {
public var step:UpdaterStep = PREPARING;
public var curFile:Int = 0;
public var files:Int = 0;
public var bytesLoaded:Float = 0;
public var bytesTotal:Float = 0;
public var downloadSpeed:Float = 0;
public var curFileName:String = "";
public var done:Bool = false;
public var curZipProgress:ZipProgress = new ZipProgress();
public function new() {}
}
@:enum
abstract UpdaterStep(Int) {
var PREPARING = 0;
var DOWNLOADING_ASSETS = 1;
var DOWNLOADING_EXECUTABLE = 2;
var INSTALLING = 3;
}
@@ -0,0 +1,152 @@
package funkin.system.updating;
import funkin.utils.MarkdownUtil;
import funkin.menus.MainMenuState;
import flixel.math.FlxPoint;
import funkin.system.updating.UpdateUtil.UpdateCheckCallback;
import funkin.system.FunkinText;
import flixel.text.FlxText;
import flixel.addons.transition.FlxTransitionableState;
import flixel.tweens.FlxTween;
class UpdateAvailableScreen extends MusicBeatState {
public var bg:FlxSprite;
public var versionCheckBG:FlxSprite;
public var title:Alphabet;
public var versionDifferenceLabel:FunkinText;
public var changeLogText:FunkinText;
public var check:UpdateCheckCallback;
public var optionsBG:FlxSprite;
public var installButton:FunkinText;
public var skipButton:FunkinText;
public var installSelected:Bool = true;
public function new(check:UpdateCheckCallback) {
super();
this.check = check;
}
public override function create() {
super.create();
FlxTransitionableState.skipNextTransIn = true;
FlxG.mouse.visible = true;
FlxG.camera.flash(0xFF000000, 0.25);
bg = new FlxSprite().loadAnimatedGraphic(Paths.image("menus/menuDesat"));
bg.color = 0xFF181818;
bg.antialiasing = true;
bg.scrollFactor.set();
bg.screenCenter();
add(bg);
title = new Alphabet(0, 10, "NEW UPDATE", true);
title.screenCenter(X);
title.scrollFactor.set();
versionDifferenceLabel = new FunkinText(0, title.y + title.height + 10, FlxG.width, '${check.currentVersionTag} < ${check.newVersionTag}', 28, false);
versionDifferenceLabel.color = 0xFFFFFFFF;
versionDifferenceLabel.alignment = CENTER;
versionDifferenceLabel.scrollFactor.set();
versionCheckBG = new FlxSprite(-1, -1).makeGraphic(1, 1, 0xFFFFFFFF);
versionCheckBG.alpha = 0.75;
versionCheckBG.color = 0xFF000000;
versionCheckBG.scale.set(FlxG.width + 2, versionDifferenceLabel.y + versionDifferenceLabel.height + 14);
versionCheckBG.updateHitbox();
versionCheckBG.scrollFactor.set();
changeLogText = new FunkinText(0, versionCheckBG.y + versionCheckBG.height + 10, FlxG.width, "", 20, true);
changeLogText.borderColor = 0xFF000000;
MarkdownUtil.applyMarkdownText(changeLogText, check.updates.last().body);
installButton = new FunkinText(0, FlxG.height - 25, Std.int(FlxG.width / 2), "> INSTALL <", 32);
skipButton = new FunkinText(Std.int(FlxG.width / 2), FlxG.height - 25, Std.int(FlxG.width / 2), "SKIP", 32);
skipButton.y -= skipButton.height;
installButton.y -= installButton.height;
installButton.alignment = skipButton.alignment = CENTER;
optionsBG = new FlxSprite(-1, installButton.y - 25).makeGraphic(1, 1, 0xFFFFFFFF);
optionsBG.alpha = 0.75;
optionsBG.color = 0xFF000000;
optionsBG.scale.set(FlxG.width + 2, Std.int(FlxG.height - optionsBG.y));
optionsBG.updateHitbox();
installButton.scrollFactor.set();
skipButton.scrollFactor.set();
optionsBG.scrollFactor.set();
add(changeLogText);
add(versionCheckBG);
add(title);
add(versionDifferenceLabel);
add(optionsBG);
add(installButton);
add(skipButton);
oldPos = FlxG.mouse.getScreenPosition();
}
var destY:Float = 0;
var oldPos:FlxPoint;
public override function update(elapsed:Float) {
super.update(elapsed);
destY = FlxMath.bound(destY - (FlxG.mouse.wheel * 75), 0, Math.max(0, changeLogText.height - FlxG.height + versionCheckBG.height + 20 + optionsBG.height));
FlxG.camera.scroll.y = lerp(FlxG.camera.scroll.y, destY, 1/3);
if (controls.LEFT_P || controls.RIGHT_P) {
installSelected = !installSelected;
changeSelection();
}
var newPos = FlxG.mouse.getScreenPosition();
if (oldPos.x != newPos.x || oldPos.y != newPos.y) {
if (newPos.y >= optionsBG.y) {
if (installSelected != (installSelected = (newPos.x < (FlxG.width / 2)))) {
changeSelection();
}
}
oldPos = newPos;
}
if (controls.ACCEPT || (newPos.y >= optionsBG.y && FlxG.mouse.justPressed))
select();
}
public function select() {
if (installSelected) {
CoolUtil.playMenuSFX(CONFIRM);
FlxG.switchState(new UpdateScreen(check));
} else {
CoolUtil.playMenuSFX(CANCEL);
FlxG.switchState(new MainMenuState());
}
}
public function changeSelection() {
CoolUtil.playMenuSFX(SCROLL, 0.7);
if (installSelected) {
installButton.text = "> INSTALL <";
skipButton.text = "SKIP";
} else {
installButton.text = "INSTALL";
skipButton.text = "> SKIP <";
}
}
public override function destroy() {
super.destroy();
FlxG.mouse.visible = false;
oldPos.put();
}
}
@@ -0,0 +1,136 @@
package funkin.system.updating;
import funkin.shaders.CustomShader;
import funkin.system.FunkinText;
import funkin.menus.TitleState;
import flixel.math.FlxRect;
import flixel.sound.FlxSound;
import flixel.ui.FlxBar;
import funkin.system.updating.UpdateUtil.UpdateCheckCallback;
class UpdateScreen extends MusicBeatState {
public var updater:AsyncUpdater;
public var progressBar:FlxBar;
public var bf:FlxSprite;
public var done:Bool = false;
public var elapsedTime:Float = 0;
public var lerpSpeed:Float = 0;
public var overSound:FlxSound;
public var generalProgress:FunkinText;
public var partProgress:FunkinText;
public var rainbowShader:CustomShader;
public function new(check:UpdateCheckCallback) {
super(false);
updater = new AsyncUpdater(check.updates);
}
public override function create() {
super.create();
progressBar = new FlxBar(0, FlxG.height - 75, LEFT_TO_RIGHT, FlxG.width, 75);
progressBar.createGradientBar([0xFF000000], [0xFF000000, 0xFF111111, 0xFF222222, 0xFF444444, 0xFF888888, -1], 1, 90);
progressBar.setRange(0, 4);
add(progressBar);
bf = new FlxSprite();
bf.antialiasing = true;
bf.frames = Paths.getFrames("menus/update/bf");
bf.animation.addByPrefix("loading", "", 0, false);
bf.animation.addByPrefix("loading-anim", "", 24, false);
bf.animation.play("loading");
bf.screenCenter();
add(bf);
partProgress = new FunkinText(0, progressBar.y, FlxG.width, "-\n-", 20);
partProgress.y -= partProgress.height;
partProgress.alignment = CENTER;
add(partProgress);
generalProgress = new FunkinText(0, partProgress.y - 10, FlxG.width, "", 32);
generalProgress.y -= generalProgress.height;
generalProgress.alignment = CENTER;
add(generalProgress);
overSound = FlxG.sound.load(Paths.sound('gameOverEnd'));
updater.execute();
FlxG.camera.addShader(rainbowShader = new CustomShader("engine/updaterShader"));
}
public override function update(elapsed:Float) {
super.update(elapsed);
elapsedTime += elapsed;
rainbowShader.hset("elapsed", elapsedTime / 3);
rainbowShader.hset("strength", (elapsedTime >= 3) ? 1 : Math.sqrt(elapsedTime / 3));
progressBar.y = FlxG.height - (65 + (Math.sin(elapsedTime * Math.PI / 2) * 10));
if (done) return;
var prog = updater.progress;
lerpSpeed = lerp(lerpSpeed, prog.downloadSpeed, 0.0625);
switch(prog.step) {
case PREPARING:
progressBar.value = 0;
generalProgress.text = "Preparing update installation... (1/4)";
partProgress.text = "Creating installation folder and cleaning old update files...";
case DOWNLOADING_ASSETS:
progressBar.value = 1 + ((prog.curFile-1+(prog.bytesLoaded/prog.bytesTotal)) / prog.files);
generalProgress.text = "Downloading update assets... (2/4)";
partProgress.text = 'Downloading file ${prog.curFileName}\n(${prog.curFile+1}/${prog.files} | ${CoolUtil.getSizeString(prog.bytesLoaded)} / ${CoolUtil.getSizeString(prog.bytesTotal)} | ${CoolUtil.getSizeString(lerpSpeed)}/s)';
case DOWNLOADING_EXECUTABLE:
progressBar.value = 2 + (prog.bytesLoaded/prog.bytesTotal);
generalProgress.text = "Downloading new engine executable... (3/4)";
partProgress.text = 'Downloading ${prog.curFileName}\n(${CoolUtil.getSizeString(prog.bytesLoaded)} / ${CoolUtil.getSizeString(prog.bytesTotal)} | ${CoolUtil.getSizeString(lerpSpeed)}/s)';
case INSTALLING:
progressBar.value = 3 + ((prog.curFile-1+(prog.curZipProgress.curFile/prog.curZipProgress.fileCount))/prog.files);
generalProgress.text = "Installing new files... (4/4)";
partProgress.text = 'Installing ${prog.curFileName}\n(${prog.curFile}/${prog.files})';
}
var rect = new FlxRect(0, (1 - (progressBar.value / 4)) * bf.frameHeight, bf.frameWidth, 0);
rect.height = bf.frameHeight - rect.y;
bf.clipRect = rect;
bf.alpha = (progressBar.value / 4) * FlxG.random.float(0.70, 0.80);
if (done = prog.done) {
// update is done, play bf's anim
FlxG.sound.music.stop();
overSound.play();
remove(generalProgress);
remove(partProgress);
generalProgress = FlxDestroyUtil.destroy(generalProgress);
partProgress = FlxDestroyUtil.destroy(partProgress);
bf.animation.curAnim.frameRate = 24;
bf.animation.play("loading-anim", true, false, 1);
bf.alpha = 1;
FlxG.camera.fade(0xFF000000, overSound.length / 1000, false, function() {
if (updater.executableReplaced) {
#if windows
// the executable has been replaced, restart the game entirely
Sys.command('start /B ${AsyncUpdater.executableName}');
#else
// We have to make the new executable allowed to execute
// before we can execute it!
Sys.command('chmod +x ./${AsyncUpdater.executableName} && ./${AsyncUpdater.executableName}');
#end
openfl.system.System.exit(0);
} else {
// assets update, switch back to TitleState.
FlxG.switchState(new TitleState());
}
});
bf.clipRect = null;
}
}
}
+105
View File
@@ -0,0 +1,105 @@
package funkin.system.updating;
import sys.FileSystem;
import haxe.io.Path;
import funkin.system.github.GitHubRelease;
import funkin.system.github.GitHub;
import lime.app.Application;
using funkin.system.github.GitHub;
class UpdateUtil {
public static final repoOwner:String = "YoshiCrafter29";
public static final repoName:String = "CodenameTestRepo";
public static function init() {
// deletes old bak file if it exists
#if sys
var bakPath = '${Path.withoutExtension(Sys.programPath())}.bak';
if (FileSystem.exists(bakPath))
FileSystem.deleteFile(bakPath);
#end
}
public static function checkForUpdates():UpdateCheckCallback {
var curTag = 'v${Application.current.meta.get('version')}';
trace(curTag);
var error = false;
var newUpdates = __doReleaseFiltering(GitHub.getReleases(repoOwner, repoName, function(e) {
error = true;
}), curTag);
if (error) return {
success: false,
newUpdate: false
};
if (newUpdates.length <= 0) {
return {
success: true,
newUpdate: false
};
}
return {
success: true,
newUpdate: true,
currentVersionTag: curTag,
newVersionTag: newUpdates.last().tag_name,
updates: newUpdates
};
}
static var __curVersionPos = -2;
static function __doReleaseFiltering(releases:Array<GitHubRelease>, currentVersionTag:String) {
releases = releases.filterReleases(Options.betaUpdates, false);
if (releases.length <= 0)
return releases;
var newArray:Array<GitHubRelease> = [];
var skipNextBinaryChecks:Bool = false;
for(index in 0...releases.length) {
var i = index;
var release = releases[i];
var containsBinary = skipNextBinaryChecks;
if (!containsBinary) {
for(asset in release.assets) {
if (asset.name.toLowerCase() == AsyncUpdater.executableGitHubName.toLowerCase()) {
containsBinary = true;
break;
}
}
}
if (containsBinary) {
skipNextBinaryChecks = true; // no need to check for older versions
if (release.tag_name == currentVersionTag) {
__curVersionPos = -1;
}
newArray.insert(0, release);
if (__curVersionPos > -2)
__curVersionPos++;
trace(release.tag_name);
}
}
if (__curVersionPos < -1)
__curVersionPos = -1;
return newArray.length <= 0 ? newArray : newArray.splice(__curVersionPos+1, newArray.length-(__curVersionPos+1));
}
}
typedef UpdateCheckCallback = {
var success:Bool;
var newUpdate:Bool;
@:optional var currentVersionTag:String;
@:optional var newVersionTag:String;
@:optional var updates:Array<GitHubRelease>;
}