moved packages (u need to update ur scripts!!!)
This commit is contained in:
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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,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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>;
|
||||
}
|
||||
Reference in New Issue
Block a user