Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions source/funkin/api/discord/DiscordClient.hx
Original file line number Diff line number Diff line change
Expand Up @@ -204,4 +204,17 @@ typedef DiscordClientPresenceParams =
*/
var ?smallImageKey:String;
}

class DiscordClientSandboxed
{
public static function setPresence(params:DiscordClientPresenceParams)
{
return DiscordClient.instance.setPresence(params);
}

public static function shutdown()
{
DiscordClient.instance.shutdown();
}
}
#end
106 changes: 106 additions & 0 deletions source/funkin/api/newgrounds/Leaderboards.hx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ package funkin.api.newgrounds;

#if FEATURE_NEWGROUNDS
import io.newgrounds.Call.CallError;
import io.newgrounds.components.ScoreBoardComponent;
import io.newgrounds.objects.Score;
import io.newgrounds.objects.ScoreBoard as LeaderboardData;
import io.newgrounds.objects.User;
import io.newgrounds.objects.events.Outcome;
import io.newgrounds.utils.ScoreBoardList;

Expand Down Expand Up @@ -66,6 +69,41 @@ class Leaderboards
}
}

/**
* Request to receive scores from Newgrounds.
* @param leaderboard The leaderboard to fetch scores from.
* @param params Additional parameters for fetching the score.
*/
public static function requestScores(leaderboard:Leaderboard, params:RequestScoresParams)
{
// Silently reject retrieving scores from unknown leaderboards.
if (leaderboard == Leaderboard.Unknown) return;

var leaderboardList = NewgroundsClient.instance.leaderboards;
if (leaderboardList == null) return;

var leaderboardData:Null<LeaderboardData> = leaderboardList.get(leaderboard.getId());
if (leaderboardData == null) return;

var user:Null<User> = null;
if ((params?.useCurrentUser ?? false) && NewgroundsClient.instance.isLoggedIn()) user = NewgroundsClient.instance.user;

leaderboardData.requestScores(params?.limit ?? 10, params?.skip ?? 0, params?.period ?? ALL, params?.social ?? false, params?.tag, user,
function(outcome:Outcome<CallError>):Void {
switch (outcome)
{
case SUCCESS:
trace('[NEWGROUNDS] Fetched scores!');
if (params?.onComplete != null) params.onComplete(leaderboardData.scores);

case FAIL(error):
trace('[NEWGROUNDS] Failed to fetch scores!');
trace(error);
if (params?.onFail != null) params.onFail();
}
});
}

/**
* Submit a score for a Story Level to Newgrounds.
*/
Expand All @@ -84,6 +122,74 @@ class Leaderboards
Leaderboards.submitScore(Leaderboard.getLeaderboardBySong(songId, difficultyId), score, tag);
}
}

/**
* Wrapper for `Leaderboards` that prevents submitting scores.
*/
@:nullSafety
class LeaderboardsSandboxed
{
public static function getLeaderboardBySong(songId:String, difficultyId:String)
{
return Leaderboard.getLeaderboardBySong(songId, difficultyId);
}

public static function getLeaderboardByLevel(levelId:String)
{
return Leaderboard.getLeaderboardByLevel(levelId);
}

public function requestScores(leaderboard:Leaderboard, params:RequestScoresParams)
{
Leaderboards.requestScores(leaderboard, params);
}
}

/**
* Additional parameters for `Leaderboards.requestScores()`
*/
typedef RequestScoresParams =
{
/**
* How many scores to include in a list.
* @default `10`
*/
var ?limit:Int;

/**
* How many scores to skip before starting the list.
* @default `0`
*/
var ?skip:Int;

/**
* The time-frame to pull the scores from.
* @default `Period.ALL`
*/
var ?period:Period;

/**
* If true, only scores by the user and their friends will be loaded. Ignored if no user is set.
* @default `false`
*/
var ?social:Bool;

/**
* An optional tag to filter the results by.
* @default `null`
*/
var ?tag:String;

/**
* If true, only the scores from the currently logged in user will be loaded.
* Additionally, if `social` is set to true, the scores of the user's friend will be loaded.
* @default `false`
*/
var ?useCurrentUser:Bool;

var ?onComplete:Array<Score>->Void;
var ?onFail:Void->Void;
}
#end

enum abstract Leaderboard(Int)
Expand Down
119 changes: 98 additions & 21 deletions source/funkin/api/newgrounds/Medals.hx
Original file line number Diff line number Diff line change
Expand Up @@ -131,33 +131,79 @@ class Medals
}
}

public static function fetchMedalData(medal:Medal):Null<FetchedMedalData>
{
var medalList = NewgroundsClient.instance.medals;
@:privateAccess
if (medalList == null || medalList._map == null) return null;

var medalData:Null<MedalData> = medalList.get(medal.getId());
@:privateAccess
if (medalData == null || medalData._data == null)
{
trace('[NEWGROUNDS] Could not retrieve data for medal: ${medal}');
return null;
}

return {
id: medalData.id,
name: medalData.name,
description: medalData.description,
icon: medalData.icon,
value: medalData.value,
difficulty: medalData.difficulty,
secret: medalData.secret,
unlocked: medalData.unlocked
}
}

public static function awardStoryLevel(id:String):Void
{
switch (id)
var medal:Medal = Medal.getMedalByStoryLevel(id);
if (medal == Medal.Unknown)
{
case 'tutorial':
Medals.award(Medal.StoryTutorial);
case 'week1':
Medals.award(Medal.StoryWeek1);
case 'week2':
Medals.award(Medal.StoryWeek2);
case 'week3':
Medals.award(Medal.StoryWeek3);
case 'week4':
Medals.award(Medal.StoryWeek4);
case 'week5':
Medals.award(Medal.StoryWeek5);
case 'week6':
Medals.award(Medal.StoryWeek6);
case 'week7':
Medals.award(Medal.StoryWeek7);
case 'weekend1':
Medals.award(Medal.StoryWeekend1);
default:
trace('[NEWGROUNDS] Story level does not have a medal! (${id}).');
trace('[NEWGROUNDS] Story level does not have a medal! (${id}).');
return;
}
Medals.award(medal);
}
}

/**
* Wrapper for `Medals` that prevents awarding medals.
*/
class MedalsSandboxed
{
public static function fetchMedalData(medal:Medal):Null<FetchedMedalData>
{
return Medals.fetchMedalData(medal);
}

public static function getMedalByStoryLevel(id:String):Medal
{
return Medal.getMedalByStoryLevel(id);
}

public static function getAllMedals():Array<Medal>
{
return Medal.getAllMedals();
}
}

/**
* Contains data for a Medal, but excludes functions like `sendUnlock()`.
*/
typedef FetchedMedalData =
{
var id:Int;
var name:String;
var description:String;
var icon:String;
var value:Int;
var difficulty:Int;
var secret:Bool;
var unlocked:Bool;
}
#end

/**
Expand Down Expand Up @@ -324,6 +370,8 @@ enum abstract Medal(Int) from Int to Int
{
switch (levelId)
{
case "tutorial":
return StoryTutorial;
case "week1":
return StoryWeek1;
case "week2":
Expand All @@ -344,4 +392,33 @@ enum abstract Medal(Int) from Int to Int
return Unknown;
}
}

/**
* Lists all medals aside from the `Unknown` one.
*/
public static function getAllMedals()
{
return [
StartGame,
StoryTutorial,
StoryWeek1,
StoryWeek2,
StoryWeek3,
StoryWeek4,
StoryWeek5,
StoryWeek6,
StoryWeek7,
StoryWeekend1,
CharSelect,
FreeplayPicoMix,
FreeplayStressPico,
LossRating,
PerfectRatingHard,
GoldPerfectRatingHard,
ErectDifficulty,
GoldPerfectRatingNightmare,
FridayNight,
Nice
];
}
}
18 changes: 18 additions & 0 deletions source/funkin/api/newgrounds/NewgroundsClient.hx
Original file line number Diff line number Diff line change
Expand Up @@ -331,4 +331,22 @@ class NewgroundsClient
return Save.instance.ngSessionId;
}
}

/**
* Wrapper for `NewgroundsClient` that prevents submitting cheated data.
*/
class NewgroundsClientSandboxed
{
public static var user(get, never):Null<User>;

static function get_user()
{
return NewgroundsClient.instance.user;
}

public static function isLoggedIn()
{
return NewgroundsClient.instance.isLoggedIn();
}
}
#end
25 changes: 16 additions & 9 deletions source/funkin/modding/PolymodHandler.hx
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,21 @@ class PolymodHandler
// `funkin.util.FileUtil` has unrestricted access to the file system.
Polymod.addImportAlias('funkin.util.FileUtil', funkin.util.FileUtilSandboxed);

#if FEATURE_NEWGROUNDS
// `funkin.api.newgrounds.Leaderboards` allows for submitting cheated scores.
Polymod.addImportAlias('funkin.api.newgrounds.Leaderboards', funkin.api.newgrounds.Leaderboards.LeaderboardsSandboxed);

// `funkin.api.newgrounds.Medals` allows for unfair granting of medals.
Polymod.addImportAlias('funkin.api.newgrounds.Medals', funkin.api.newgrounds.Medals.MedalsSandboxed);

// `funkin.api.newgrounds.NewgroundsClientSandboxed` allows for submitting cheated data.
Polymod.addImportAlias('funkin.api.newgrounds.NewgroundsClient', funkin.api.newgrounds.NewgroundsClient.NewgroundsClientSandboxed);
#end

#if FEATURE_DISCORD_RPC
Polymod.addImportAlias('funkin.api.discord.DiscordClient', funkin.api.discord.DiscordClient.DiscordClientSandboxed);
#end

// Add blacklisting for prohibited classes and packages.

// `Sys`
Expand Down Expand Up @@ -310,6 +325,7 @@ class PolymodHandler
{
if (cls == null) continue;
var className:String = Type.getClassName(cls);
if (polymod.hscript._internal.PolymodScriptClass.importOverrides.exists(className)) continue;
Polymod.blacklistImport(className);
}

Expand All @@ -322,15 +338,6 @@ class PolymodHandler
Polymod.blacklistImport(className);
}

// `funkin.api.newgrounds.*`
// Contains functions which allow for cheating medals and leaderboards.
for (cls in ClassMacro.listClassesInPackage('funkin.api.newgrounds'))
{
if (cls == null) continue;
var className:String = Type.getClassName(cls);
Polymod.blacklistImport(className);
}

// `io.newgrounds.*`
// Contains functions which allow for cheating medals and leaderboards.
for (cls in ClassMacro.listClassesInPackage('io.newgrounds'))
Expand Down
Loading