Skip to content
Draft
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
2 changes: 2 additions & 0 deletions core/src/bms/player/beatoraja/MainController.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import bms.player.beatoraja.exceptions.PlayerConfigException;
import bms.player.beatoraja.modmenu.ImGuiRenderer;
import bms.player.beatoraja.modmenu.fm.SongManagerMenu;
import com.badlogic.gdx.*;
import com.badlogic.gdx.graphics.*;
import com.badlogic.gdx.graphics.g2d.*;
Expand Down Expand Up @@ -355,6 +356,7 @@ public void create() {
streamController = new StreamController(selector, (player.getRequestNotify() ? messageRenderer : null));
streamController.run();
}
SongManagerMenu.injectMusicSelector(selector);
decide = new MusicDecide(this);
result = new MusicResult(this);
gresult = new CourseResult(this);
Expand Down
19 changes: 15 additions & 4 deletions core/src/bms/player/beatoraja/modmenu/ImGuiRenderer.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package bms.player.beatoraja.modmenu;

import bms.player.beatoraja.controller.Lwjgl3ControllerManager;
import bms.player.beatoraja.modmenu.fm.SongManagerMenu;
import bms.player.beatoraja.modmenu.fm.FolderManagerMenu;

import bms.player.beatoraja.select.MusicSelector;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Graphics;
Expand Down Expand Up @@ -35,8 +38,9 @@ public class ImGuiRenderer {

private static ImBoolean SHOW_MOD_MENU = new ImBoolean(false);
private static ImBoolean SHOW_RANDOM_TRAINER = new ImBoolean(false);

private static ImBoolean SHOW_FREQ_PLUS = new ImBoolean(false);
private static ImBoolean SHOW_FOLDER_MANAGER = new ImBoolean(false);
private static ImBoolean SHOW_SONG_MANAGER = new ImBoolean(false);


public static void init() {
Expand Down Expand Up @@ -77,7 +81,7 @@ public static void init() {
public static void start() {
if (tmpProcessor != null) {
Gdx.input.setInputProcessor(tmpProcessor);
tmpProcessor = null;
tmpProcessor = null;
}
imGuiGlfw.newFrame();
ImGui.newFrame();
Expand All @@ -94,13 +98,21 @@ public static void render() {

ImGui.checkbox("Show Rate Modifier Window", SHOW_FREQ_PLUS);
ImGui.checkbox("Show Random Trainer Window", SHOW_RANDOM_TRAINER);
ImGui.checkbox("Show Folder Manager Menu", SHOW_FOLDER_MANAGER);
ImGui.checkbox("Show Song Manager Menu", SHOW_SONG_MANAGER);

if (SHOW_FREQ_PLUS.get()) {
FreqTrainerMenu.show(SHOW_FREQ_PLUS);
}
if (SHOW_RANDOM_TRAINER.get()) {
RandomTrainerMenu.show(SHOW_RANDOM_TRAINER);
}

if (SHOW_FOLDER_MANAGER.get()) {
FolderManagerMenu.show(SHOW_FOLDER_MANAGER);
}
if (SHOW_SONG_MANAGER.get()) {
SongManagerMenu.show(SHOW_SONG_MANAGER);
}

if (ImGui.treeNode("Controller Input Debug Information")) {
float axis;
Expand Down Expand Up @@ -149,7 +161,6 @@ public static void helpMarker(String desc) {
ImGui.popTextWrapPos();
ImGui.endTooltip();
}

}


Expand Down
98 changes: 98 additions & 0 deletions core/src/bms/player/beatoraja/modmenu/fm/FolderDefinition.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package bms.player.beatoraja.modmenu.fm;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;

import java.util.List;

/*
* Represents one folder definition in folder/default.json
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
public class FolderDefinition {
private String sql;
private String name;
@JsonProperty("showall")
private Boolean showAll;
@JsonProperty("folder")
private List<FolderDefinition> children;

// NOTE: This field can be viewed as key
@JsonIgnore
private Integer bits;

public FolderDefinition(String sql, String name, Boolean showAll, Integer bits) {
this.sql = sql;
this.name = name;
this.showAll = showAll;
this.bits = bits;
}

public FolderDefinition(String sql, String name, Boolean showAll) {
this(sql, name, showAll, null);
}

public FolderDefinition() {
this(null, null, null, null);
}

/**
* If sql field satisfy 'favorite & x != 0' pattern, set corresponding bits field
* Otherwise do nothing
*
* @implSpec Do not throw error even if sql field is corrupted or cannot take bits from it
*/
public void tryExtractBitsFromSql() {
if (sql == null || sql.isEmpty()) {
return ;
}
String left = "favorite & ";
String right = " != 0";
if (!sql.startsWith(left) || !sql.endsWith(right)) {
return ;
}
String expectedStr = sql.substring(left.length(), sql.length() - right.length());
if (expectedStr.isEmpty()) {
return ;
}
try {
int pw = Integer.parseInt(expectedStr);
this.bits = 31 - Integer.numberOfLeadingZeros(pw);
} catch (Exception e) {
// Do nothing
}
}

public String getSql() {
return sql;
}

public String getName() {
return name;
}

public Boolean getShowAll() {
return showAll;
}

public Integer getBits() {
return bits;
}

public void setSql(String sql) {
this.sql = sql;
}

public void setName(String name) {
this.name = name;
}

public void setShowAll(Boolean showAll) {
this.showAll = showAll;
}

public void setBits(Integer bits) {
this.bits = bits;
}
}
155 changes: 155 additions & 0 deletions core/src/bms/player/beatoraja/modmenu/fm/FolderManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package bms.player.beatoraja.modmenu.fm;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

/**
* NOTE: FolderManager can only manage folders which sql is based on favorite field
* Expect some low-level functions, you should never use FOLDER_DEFINITIONS directly
* or try to modify the result from getFolderDefinitions()
*/
public class FolderManager {
private static final String FILE_LOCATION = "folder/default.json";
private static List<FolderDefinition> FOLDER_DEFINITIONS = new ArrayList<>();

//@formatter:off
static {
try {
ObjectMapper om = new ObjectMapper();
List<FolderDefinition> fds = om.readValue(new BufferedInputStream(Files.newInputStream(Paths.get(FILE_LOCATION))), new TypeReference<>(){});
// Hack: extract the bits field from sql for not breaking the compatibility
fds.forEach(fd -> {
fd.tryExtractBitsFromSql();
FOLDER_DEFINITIONS.add(fd);
});
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
//@formatter:on

// NOTE: Here's a limitation from 'favorite' length (26+4=30 <= 31)
private static final int MAXIMUM_FOLDER_COUNT = 5;

public static List<FolderDefinition> getFolderDefinitions() {
return FOLDER_DEFINITIONS.stream().filter(fd -> fd.getBits() != null).toList();
}

/**
* Persist fds to folder/default.json
*/
public static void persist(List<FolderDefinition> fds) throws IOException {
ObjectMapper om = new ObjectMapper();
om.writeValue(new BufferedOutputStream(Files.newOutputStream(Paths.get(FILE_LOCATION))), fds);
}

/**
* Save one folder definition<br>
* <li>Update FOLDER_DEFINITIONS</li>
* <li>Persist current data to disk</li>
*
* @implSpec If persist failed, FOLDER_DEFINITIONS must keep the same
*/
private static void save(FolderDefinition folderDefinition) throws IOException {
try {
// Copy entire list for persisting
List<FolderDefinition> copy = new ArrayList<>(FOLDER_DEFINITIONS);
copy.add(folderDefinition);
persist(copy);
} catch (Exception e) {
e.printStackTrace();
throw e;
}
FOLDER_DEFINITIONS.add(folderDefinition);
}

private static void remove(int bits) throws IOException {
Predicate<FolderDefinition> equalsOnBits = fd -> fd.getBits() != null && fd.getBits().equals(bits);
try {
List<FolderDefinition> copy = new ArrayList<>(FOLDER_DEFINITIONS);
if (!copy.removeIf(equalsOnBits)) {
return ; // Okay dokey
}
persist(copy);
} catch (Exception e) {
e.printStackTrace();
throw e;
}
FOLDER_DEFINITIONS.removeIf(equalsOnBits);
}

/**
* Create a new folder and persist state to disk
*
* @param name folder name
* @throws IllegalStateException if there is no room for folder or something bad happens
*/
public static void newFolder(String name) throws IllegalStateException {
if (getFolderDefinitions().size() >= MAXIMUM_FOLDER_COUNT) {
throw new IllegalStateException("Failed to save changes: folder count cannot exceed to " + MAXIMUM_FOLDER_COUNT);
}
List<Integer> usedBits = getFolderDefinitions().stream().map(FolderDefinition::getBits).toList();
int nextBit = findMex(usedBits);
FolderDefinition fd = new FolderDefinition(generateSql(nextBit), name, false, nextBit);
try {
save(fd);
} catch (Exception e) {
throw new IllegalStateException("Failed to save changes: " + e.getMessage());
}
}

/**
* Remove specified folder and persist current state to disk
*
* @throws IllegalStateException if the folder is not exist or something bad happens
*/
public static void removeFolder(int bits) throws IllegalStateException {
try {
remove(bits);
} catch (Exception e) {
throw new IllegalStateException("Failed to save changes: " + e.getMessage());
}
}

/**
* Get the mex from an array of integers
* MEX: The minimum integer x satisfy "x >= 0 and x doesn't appear in arr"
*
* @return MEX of arr
*/
private static int findMex(List<Integer> arr) {
int mex = 0;
// No need for sort
while (true) {
boolean noProgress = true;
for (Integer x : arr) {
if (x.equals(mex)) {
noProgress = false;
mex++;
break;
}
}
if (noProgress) {
break;
}
}
return mex;
}

/**
* @return `favorite & {bits} != 0`
*/
private static String generateSql(Integer bits) {
return String.format("favorite & %d != 0", 1 << bits);
}
}
Loading