Merge branch 'development' into unstable

# Conflicts:
#	src/main/java/emu/grasscutter/command/commands/SetPropCommand.java
This commit is contained in:
KingRainbow44 2023-04-24 20:58:14 -04:00
commit 18124bd053
No known key found for this signature in database
GPG Key ID: FC2CB64B00D257BE
2 changed files with 281 additions and 287 deletions

View File

@ -20,7 +20,7 @@
## Quick setup guide
**Note:** For support please join our [Discord](https://discord.gg/T5vZU6UyeG).
**Note**: For support please join our [Discord](https://discord.gg/T5vZU6UyeG).
### Requirements
@ -86,7 +86,7 @@ cd Grasscutter
.\gradlew jar # Compile
```
##### Linux
##### Linux (GNU)
```bash
git clone https://github.com/Grasscutters/Grasscutter.git
@ -97,10 +97,6 @@ chmod +x gradlew
You can find the output jar in the root of the project folder.
### Commands have moved to the [wiki](https://github.com/Grasscutters/Grasscutter/wiki/Commands)!
### Troubleshooting
# Quick Troubleshooting
* If compiling wasn't successful, please check your JDK installation (Make sure its JDK 17 or higher and validated JDK's bin PATH variable).
* My client doesn't connect, doesn't login, 4206, etc... - Mostly your proxy daemon setup is *the issue*. If you're using Fiddler, change the default port to anything other than 8888.
* Startup sequence: MongoDB > Grasscutter > Proxy Daemon (mitmdump, fiddler, etc.) > Game
For a list of common issues and solutions and to ask for help, please join [our Discord server](https://discord.gg/T5vZU6UyeG) and go to the support channel.

View File

@ -1,279 +1,277 @@
package emu.grasscutter.command.commands;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.data.GameData;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.tower.TowerLevelRecord;
import emu.grasscutter.server.packet.send.PacketOpenStateChangeNotify;
import emu.grasscutter.server.packet.send.PacketSceneAreaUnlockNotify;
import emu.grasscutter.server.packet.send.PacketScenePointUnlockNotify;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Command(
label = "setProp",
aliases = {"prop"},
usage = {"<prop> <value>"},
permission = "player.setprop",
permissionTargeted = "player.setprop.others")
public final class SetPropCommand implements CommandHandler {
// List of map areas. Unfortunately, there is no readily available source for them in excels or
// bins.
private static final List<Integer> sceneAreas =
List.of(
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
28, 29, 32, 100, 101, 102, 103, 200, 210, 300, 400, 401, 402, 403);
Map<String, Prop> props;
public SetPropCommand() {
this.props = new HashMap<>();
// Full PlayerProperty enum that won't be advertised but can be used by devs
for (PlayerProperty prop : PlayerProperty.values()) {
String name = prop.toString().substring(5); // PROP_EXP -> EXP
String key = name.toLowerCase(); // EXP -> exp
this.props.put(key, new Prop(name, prop));
}
// Add special props
Prop worldlevel =
new Prop("World Level", PlayerProperty.PROP_PLAYER_WORLD_LEVEL, PseudoProp.WORLD_LEVEL);
this.props.put("worldlevel", worldlevel);
this.props.put("wl", worldlevel);
Prop abyss = new Prop("Tower Level", PseudoProp.TOWER_LEVEL);
this.props.put("abyss", abyss);
this.props.put("abyssfloor", abyss);
this.props.put("ut", abyss);
this.props.put("tower", abyss);
this.props.put("towerlevel", abyss);
this.props.put("unlocktower", abyss);
Prop bplevel = new Prop("BP Level", PseudoProp.BP_LEVEL);
this.props.put("bplevel", bplevel);
this.props.put("bp", bplevel);
this.props.put("battlepass", bplevel);
Prop godmode = new Prop("GodMode", PseudoProp.GOD_MODE);
this.props.put("godmode", godmode);
this.props.put("god", godmode);
Prop nostamina = new Prop("UnlimitedStamina", PseudoProp.UNLIMITED_STAMINA);
this.props.put("unlimitedstamina", nostamina);
this.props.put("us", nostamina);
this.props.put("nostamina", nostamina);
this.props.put("nostam", nostamina);
this.props.put("ns", nostamina);
Prop unlimitedenergy = new Prop("UnlimitedEnergy", PseudoProp.UNLIMITED_ENERGY);
this.props.put("unlimitedenergy", unlimitedenergy);
this.props.put("ue", unlimitedenergy);
Prop setopenstate = new Prop("SetOpenstate", PseudoProp.SET_OPENSTATE);
this.props.put("setopenstate", setopenstate);
this.props.put("so", setopenstate);
Prop unsetopenstate = new Prop("UnsetOpenstate", PseudoProp.UNSET_OPENSTATE);
this.props.put("unsetopenstate", unsetopenstate);
this.props.put("uo", unsetopenstate);
Prop unlockmap = new Prop("UnlockMap", PseudoProp.UNLOCK_MAP);
this.props.put("unlockmap", unlockmap);
this.props.put("um", unlockmap);
}
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.size() != 2) {
sendUsageMessage(sender);
return;
}
String propStr = args.get(0).toLowerCase();
String valueStr = args.get(1).toLowerCase();
int value;
if (!props.containsKey(propStr)) {
sendUsageMessage(sender);
return;
}
try {
value =
switch (valueStr.toLowerCase()) {
case "on", "true" -> 1;
case "off", "false" -> 0;
case "toggle" -> -1;
default -> Integer.parseInt(valueStr);
};
} catch (NumberFormatException ignored) {
CommandHandler.sendTranslatedMessage(sender, "commands.execution.argument_error");
return;
}
boolean success = false;
Prop prop = props.get(propStr);
success =
switch (prop.pseudoProp) {
case WORLD_LEVEL -> targetPlayer.setWorldLevel(value);
case BP_LEVEL -> targetPlayer.getBattlePassManager().setLevel(value);
case TOWER_LEVEL -> this.setTowerLevel(sender, targetPlayer, value);
case GOD_MODE, UNLIMITED_STAMINA, UNLIMITED_ENERGY -> this.setBool(
sender, targetPlayer, prop.pseudoProp, value);
case SET_OPENSTATE -> this.setOpenState(targetPlayer, value, 1);
case UNSET_OPENSTATE -> this.setOpenState(targetPlayer, value, 0);
case UNLOCK_MAP -> unlockMap(targetPlayer);
default -> targetPlayer.setProperty(prop.prop, value);
};
if (success) {
if (targetPlayer == sender) {
CommandHandler.sendTranslatedMessage(
sender, "commands.generic.set_to", prop.name, valueStr);
} else {
String uidStr = targetPlayer.getAccount().getId();
CommandHandler.sendTranslatedMessage(
sender, "commands.generic.set_for_to", prop.name, uidStr, valueStr);
}
} else {
if (prop.prop
!= PlayerProperty.PROP_NONE) { // PseudoProps need to do their own error messages
int min = targetPlayer.getPropertyMin(prop.prop);
int max = targetPlayer.getPropertyMax(prop.prop);
CommandHandler.sendTranslatedMessage(
sender, "commands.generic.invalid.value_between", prop.name, min, max);
}
}
}
private boolean setTowerLevel(Player sender, Player targetPlayer, int topFloor) {
List<Integer> floorIds = targetPlayer.getServer().getTowerSystem().getAllFloors();
if (topFloor < 0 || topFloor > floorIds.size()) {
CommandHandler.sendTranslatedMessage(
sender, "commands.generic.invalid.value_between", "Tower Level", 0, floorIds.size());
return false;
}
Map<Integer, TowerLevelRecord> recordMap = targetPlayer.getTowerManager().getRecordMap();
// Add records for each unlocked floor
for (int floor : floorIds.subList(0, topFloor)) {
if (!recordMap.containsKey(floor)) {
recordMap.put(floor, new TowerLevelRecord(floor));
}
}
// Remove records for each floor past our target
for (int floor : floorIds.subList(topFloor, floorIds.size())) {
recordMap.remove(floor);
}
// Six stars required on Floor 8 to unlock Floor 9+
if (topFloor > 8) {
recordMap
.get(floorIds.get(7))
.setLevelStars(
0,
6); // levelIds seem to start at 1 for Floor 1 Chamber 1, so this doesn't get shown at
// all
}
return true;
}
private boolean setBool(Player sender, Player targetPlayer, PseudoProp pseudoProp, int value) {
boolean enabled =
switch (pseudoProp) {
case GOD_MODE -> targetPlayer.inGodmode();
case UNLIMITED_STAMINA -> targetPlayer.getUnlimitedStamina();
case UNLIMITED_ENERGY -> !targetPlayer.getEnergyManager().getEnergyUsage();
default -> false;
};
enabled =
switch (value) {
case -1 -> !enabled;
case 0 -> false;
default -> true;
};
switch (pseudoProp) {
case GOD_MODE:
targetPlayer.setGodmode(enabled);
break;
case UNLIMITED_STAMINA:
targetPlayer.setUnlimitedStamina(enabled);
break;
case UNLIMITED_ENERGY:
targetPlayer.getEnergyManager().setEnergyUsage(!enabled);
break;
default:
return false;
}
return true;
}
private boolean setOpenState(Player targetPlayer, int state, int value) {
targetPlayer.sendPacket(new PacketOpenStateChangeNotify(state, value));
return true;
}
private boolean unlockMap(Player targetPlayer) {
// Unlock.
GameData.getScenePointsPerScene()
.forEach(
(sceneId, scenePoints) -> {
// Unlock trans points.
targetPlayer.getUnlockedScenePoints(sceneId).addAll(scenePoints);
// Unlock map areas.
targetPlayer.getUnlockedSceneAreas(sceneId).addAll(sceneAreas);
});
// Send notify.
int playerScene = targetPlayer.getSceneId();
targetPlayer.sendPacket(
new PacketScenePointUnlockNotify(
playerScene, targetPlayer.getUnlockedScenePoints(playerScene)));
targetPlayer.sendPacket(
new PacketSceneAreaUnlockNotify(
playerScene, targetPlayer.getUnlockedSceneAreas(playerScene)));
return true;
}
enum PseudoProp {
NONE,
WORLD_LEVEL,
TOWER_LEVEL,
BP_LEVEL,
GOD_MODE,
UNLIMITED_STAMINA,
UNLIMITED_ENERGY,
SET_OPENSTATE,
UNSET_OPENSTATE,
UNLOCK_MAP
}
static class Prop {
String name;
PlayerProperty prop;
PseudoProp pseudoProp;
public Prop(PlayerProperty prop) {
this(prop.toString(), prop, PseudoProp.NONE);
}
public Prop(String name) {
this(name, PlayerProperty.PROP_NONE, PseudoProp.NONE);
}
public Prop(String name, PseudoProp pseudoProp) {
this(name, PlayerProperty.PROP_NONE, pseudoProp);
}
public Prop(String name, PlayerProperty prop) {
this(name, prop, PseudoProp.NONE);
}
public Prop(String name, PlayerProperty prop, PseudoProp pseudoProp) {
this.name = name;
this.prop = prop;
this.pseudoProp = pseudoProp;
}
}
}
package emu.grasscutter.command.commands;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.IntStream;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.data.GameData;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.tower.TowerLevelRecord;
import emu.grasscutter.server.packet.send.PacketOpenStateChangeNotify;
import emu.grasscutter.server.packet.send.PacketSceneAreaUnlockNotify;
import emu.grasscutter.server.packet.send.PacketScenePointUnlockNotify;
@Command(
label = "setProp",
aliases = {"prop"},
usage = {"<prop> <value>"},
permission = "player.setprop",
permissionTargeted = "player.setprop.others")
public final class SetPropCommand implements CommandHandler {
// List of map areas. Unfortunately, there is no readily available source for them in excels or
// bins.
private static final List<Integer> sceneAreas = IntStream.range(1, 1000).boxed().toList();Map<String, Prop> props;
public SetPropCommand() {
this.props = new HashMap<>();
// Full PlayerProperty enum that won't be advertised but can be used by devs
for (PlayerProperty prop : PlayerProperty.values()) {
String name = prop.toString().substring(5); // PROP_EXP -> EXP
String key = name.toLowerCase(); // EXP -> exp
this.props.put(key, new Prop(name, prop));
}
// Add special props
Prop worldlevel =
new Prop("World Level", PlayerProperty.PROP_PLAYER_WORLD_LEVEL, PseudoProp.WORLD_LEVEL);
this.props.put("worldlevel", worldlevel);
this.props.put("wl", worldlevel);
Prop abyss = new Prop("Tower Level", PseudoProp.TOWER_LEVEL);
this.props.put("abyss", abyss);
this.props.put("abyssfloor", abyss);
this.props.put("ut", abyss);
this.props.put("tower", abyss);
this.props.put("towerlevel", abyss);
this.props.put("unlocktower", abyss);
Prop bplevel = new Prop("BP Level", PseudoProp.BP_LEVEL);
this.props.put("bplevel", bplevel);
this.props.put("bp", bplevel);
this.props.put("battlepass", bplevel);
Prop godmode = new Prop("GodMode", PseudoProp.GOD_MODE);
this.props.put("godmode", godmode);
this.props.put("god", godmode);
Prop nostamina = new Prop("UnlimitedStamina", PseudoProp.UNLIMITED_STAMINA);
this.props.put("unlimitedstamina", nostamina);
this.props.put("us", nostamina);
this.props.put("nostamina", nostamina);
this.props.put("nostam", nostamina);
this.props.put("ns", nostamina);
Prop unlimitedenergy = new Prop("UnlimitedEnergy", PseudoProp.UNLIMITED_ENERGY);
this.props.put("unlimitedenergy", unlimitedenergy);
this.props.put("ue", unlimitedenergy);
Prop setopenstate = new Prop("SetOpenstate", PseudoProp.SET_OPENSTATE);
this.props.put("setopenstate", setopenstate);
this.props.put("so", setopenstate);
Prop unsetopenstate = new Prop("UnsetOpenstate", PseudoProp.UNSET_OPENSTATE);
this.props.put("unsetopenstate", unsetopenstate);
this.props.put("uo", unsetopenstate);
Prop unlockmap = new Prop("UnlockMap", PseudoProp.UNLOCK_MAP);
this.props.put("unlockmap", unlockmap);
this.props.put("um", unlockmap);
}
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.size() != 2) {
sendUsageMessage(sender);
return;
}
String propStr = args.get(0).toLowerCase();
String valueStr = args.get(1).toLowerCase();
int value;
if (!props.containsKey(propStr)) {
sendUsageMessage(sender);
return;
}
try {
value =
switch (valueStr.toLowerCase()) {
case "on", "true" -> 1;
case "off", "false" -> 0;
case "toggle" -> -1;
default -> Integer.parseInt(valueStr);
};
} catch (NumberFormatException ignored) {
CommandHandler.sendTranslatedMessage(sender, "commands.execution.argument_error");
return;
}
boolean success = false;
Prop prop = props.get(propStr);
success =
switch (prop.pseudoProp) {
case WORLD_LEVEL -> targetPlayer.setWorldLevel(value);
case BP_LEVEL -> targetPlayer.getBattlePassManager().setLevel(value);
case TOWER_LEVEL -> this.setTowerLevel(sender, targetPlayer, value);
case GOD_MODE, UNLIMITED_STAMINA, UNLIMITED_ENERGY -> this.setBool(
sender, targetPlayer, prop.pseudoProp, value);
case SET_OPENSTATE -> this.setOpenState(targetPlayer, value, 1);
case UNSET_OPENSTATE -> this.setOpenState(targetPlayer, value, 0);
case UNLOCK_MAP -> unlockMap(targetPlayer);
default -> targetPlayer.setProperty(prop.prop, value);
};
if (success) {
if (targetPlayer == sender) {
CommandHandler.sendTranslatedMessage(
sender, "commands.generic.set_to", prop.name, valueStr);
} else {
String uidStr = targetPlayer.getAccount().getId();
CommandHandler.sendTranslatedMessage(
sender, "commands.generic.set_for_to", prop.name, uidStr, valueStr);
}
} else {
if (prop.prop
!= PlayerProperty.PROP_NONE) { // PseudoProps need to do their own error messages
int min = targetPlayer.getPropertyMin(prop.prop);
int max = targetPlayer.getPropertyMax(prop.prop);
CommandHandler.sendTranslatedMessage(
sender, "commands.generic.invalid.value_between", prop.name, min, max);
}
}
}
private boolean setTowerLevel(Player sender, Player targetPlayer, int topFloor) {
List<Integer> floorIds = targetPlayer.getServer().getTowerSystem().getAllFloors();
if (topFloor < 0 || topFloor > floorIds.size()) {
CommandHandler.sendTranslatedMessage(
sender, "commands.generic.invalid.value_between", "Tower Level", 0, floorIds.size());
return false;
}
Map<Integer, TowerLevelRecord> recordMap = targetPlayer.getTowerManager().getRecordMap();
// Add records for each unlocked floor
for (int floor : floorIds.subList(0, topFloor)) {
if (!recordMap.containsKey(floor)) {
recordMap.put(floor, new TowerLevelRecord(floor));
}
}
// Remove records for each floor past our target
for (int floor : floorIds.subList(topFloor, floorIds.size())) {
recordMap.remove(floor);
}
// Six stars required on Floor 8 to unlock Floor 9+
if (topFloor > 8) {
recordMap
.get(floorIds.get(7))
.setLevelStars(
0,
6); // levelIds seem to start at 1 for Floor 1 Chamber 1, so this doesn't get shown at
// all
}
return true;
}
private boolean setBool(Player sender, Player targetPlayer, PseudoProp pseudoProp, int value) {
boolean enabled =
switch (pseudoProp) {
case GOD_MODE -> targetPlayer.inGodmode();
case UNLIMITED_STAMINA -> targetPlayer.getUnlimitedStamina();
case UNLIMITED_ENERGY -> !targetPlayer.getEnergyManager().getEnergyUsage();
default -> false;
};
enabled =
switch (value) {
case -1 -> !enabled;
case 0 -> false;
default -> true;
};
switch (pseudoProp) {
case GOD_MODE:
targetPlayer.setGodmode(enabled);
break;
case UNLIMITED_STAMINA:
targetPlayer.setUnlimitedStamina(enabled);
break;
case UNLIMITED_ENERGY:
targetPlayer.getEnergyManager().setEnergyUsage(!enabled);
break;
default:
return false;
}
return true;
}
private boolean setOpenState(Player targetPlayer, int state, int value) {
targetPlayer.sendPacket(new PacketOpenStateChangeNotify(state, value));
return true;
}
private boolean unlockMap(Player targetPlayer) {
// Unlock.
GameData.getScenePointsPerScene()
.forEach(
(sceneId, scenePoints) -> {
// Unlock trans points.
targetPlayer.getUnlockedScenePoints(sceneId).addAll(scenePoints);
// Unlock map areas.
targetPlayer.getUnlockedSceneAreas(sceneId).addAll(sceneAreas);
});
// Send notify.
int playerScene = targetPlayer.getSceneId();
targetPlayer.sendPacket(
new PacketScenePointUnlockNotify(
playerScene, targetPlayer.getUnlockedScenePoints(playerScene)));
targetPlayer.sendPacket(
new PacketSceneAreaUnlockNotify(
playerScene, targetPlayer.getUnlockedSceneAreas(playerScene)));
return true;
}
enum PseudoProp {
NONE,
WORLD_LEVEL,
TOWER_LEVEL,
BP_LEVEL,
GOD_MODE,
UNLIMITED_STAMINA,
UNLIMITED_ENERGY,
SET_OPENSTATE,
UNSET_OPENSTATE,
UNLOCK_MAP
}
static class Prop {
String name;
PlayerProperty prop;
PseudoProp pseudoProp;
public Prop(PlayerProperty prop) {
this(prop.toString(), prop, PseudoProp.NONE);
}
public Prop(String name) {
this(name, PlayerProperty.PROP_NONE, PseudoProp.NONE);
}
public Prop(String name, PseudoProp pseudoProp) {
this(name, PlayerProperty.PROP_NONE, pseudoProp);
}
public Prop(String name, PlayerProperty prop) {
this(name, prop, PseudoProp.NONE);
}
public Prop(String name, PlayerProperty prop, PseudoProp pseudoProp) {
this.name = name;
this.prop = prop;
this.pseudoProp = pseudoProp;
}
}
}