Merge branch '1.20.4' into 1.20.4-dev
This commit is contained in:
commit
89b48fc3ff
39
.github/workflows/backport-prs.yml
vendored
Normal file
39
.github/workflows/backport-prs.yml
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
name: Backport merged pull request
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [closed]
|
||||
issue_comment:
|
||||
types: [created]
|
||||
permissions:
|
||||
contents: write # so it can comment
|
||||
pull-requests: write # so it can create pull requests
|
||||
jobs:
|
||||
backport:
|
||||
name: Backport pull request
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# Only run when pull request is merged
|
||||
# or when a comment starting with `/backport` is created by someone other than the
|
||||
# https://github.com/backport-action bot user (user id: 97796249). Note that if you use your
|
||||
# own PAT as `github_token`, that you should replace this id with yours.
|
||||
if: >
|
||||
(
|
||||
github.event_name == 'pull_request_target' &&
|
||||
github.event.pull_request.merged
|
||||
) || (
|
||||
github.event_name == 'issue_comment' &&
|
||||
github.event.issue.pull_request &&
|
||||
github.event.comment.user.id != 97796249 &&
|
||||
startsWith(github.event.comment.body, '/backport')
|
||||
)
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Create backport pull requests
|
||||
uses: korthout/backport-action@v3
|
||||
with:
|
||||
github_token: ${{ secrets.TOKEN }}
|
||||
pull_description: |
|
||||
Backport of #${pull_number} to `${target_branch}`.
|
||||
|
||||
### Description
|
||||
${pull_description}
|
||||
10
.vscode/settings.json
vendored
Normal file
10
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"java.compile.nullAnalysis.mode": "automatic",
|
||||
"java.completion.importOrder": [
|
||||
"",
|
||||
"javax",
|
||||
"java",
|
||||
"#"
|
||||
],
|
||||
"java.sources.organizeImports.starThreshold": 5
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package vip.fubuki.playersync;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.neoforged.bus.api.IEventBus;
|
||||
import net.neoforged.bus.api.SubscribeEvent;
|
||||
import net.neoforged.fml.ModList;
|
||||
|
|
@ -50,7 +51,7 @@ public class PlayerSync {
|
|||
String dbName = JdbcConfig.DATABASE_NAME.get();
|
||||
|
||||
// Step 1: Create the database using a connection that does not select a database.
|
||||
JDBCsetUp.executeUpdate("CREATE DATABASE IF NOT EXISTS " + dbName, 1);
|
||||
JDBCsetUp.executeUpdateWithoutDatabase("CREATE DATABASE IF NOT EXISTS " + dbName);
|
||||
|
||||
// Step 2: Explicitly select the database on a connection obtained without default database.
|
||||
try (Connection conn = JDBCsetUp.getConnection(false);
|
||||
|
|
@ -112,17 +113,38 @@ public class PlayerSync {
|
|||
"PRIMARY KEY (`id`)" +
|
||||
");"
|
||||
);
|
||||
// do not modify the create table statement to make sure this code is compatible with older database versions
|
||||
addColumnIfNotExists("server_info", "data_version", "INT NOT NULL DEFAULT 0");
|
||||
|
||||
long current = System.currentTimeMillis();
|
||||
JDBCsetUp.executeUpdate(
|
||||
"INSERT INTO " + dbName + ".server_info(id,enable,last_update) " +
|
||||
"VALUES(" + JdbcConfig.SERVER_ID.get() + ",true," + current + ") " +
|
||||
"ON DUPLICATE KEY UPDATE id= " + JdbcConfig.SERVER_ID.get() + ",enable = 1," +
|
||||
"last_update=" + current + ";"
|
||||
);
|
||||
JDBCsetUp.executeUpdate(
|
||||
"UPDATE " + dbName + ".server_info SET last_update=" + System.currentTimeMillis() +
|
||||
" WHERE id='" + JdbcConfig.SERVER_ID.get() + "'"
|
||||
);
|
||||
int data_version = SharedConstants.getCurrentVersion().getDataVersion().getVersion();
|
||||
JDBCsetUp.executeUpdate("""
|
||||
INSERT INTO %s.server_info
|
||||
(
|
||||
id,
|
||||
enable,
|
||||
data_version,
|
||||
last_update
|
||||
)
|
||||
VALUES (
|
||||
%d,
|
||||
true,
|
||||
%d,
|
||||
%d
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
id = %d,
|
||||
enable = true,
|
||||
data_version = %d,
|
||||
last_update = %d;
|
||||
""",
|
||||
dbName,
|
||||
JdbcConfig.SERVER_ID.get(),
|
||||
data_version,
|
||||
current,
|
||||
JdbcConfig.SERVER_ID.get(),
|
||||
data_version,
|
||||
current);
|
||||
|
||||
// Create curios table if the Curios mod is loaded
|
||||
if (ModList.get().isLoaded("curios")) {
|
||||
|
|
@ -135,28 +157,13 @@ public class PlayerSync {
|
|||
|
||||
// Create backpack_data table
|
||||
if (ModList.get().isLoaded("sophisticatedbackpacks")) {
|
||||
JDBCsetUp.executeUpdate(
|
||||
JDBCsetUp.executeUpdateWithoutDatabase(
|
||||
"CREATE TABLE IF NOT EXISTS " + dbName + ".backpack_data (" +
|
||||
"uuid CHAR(36) NOT NULL, backpack_nbt MEDIUMBLOB, PRIMARY KEY (uuid)" +
|
||||
");", 1
|
||||
");"
|
||||
);
|
||||
|
||||
// Check if backpack_data table has the 'uuid' column
|
||||
JDBCsetUp.QueryResult backpackColCheck = JDBCsetUp.executeQuery(
|
||||
"SELECT COUNT(*) AS colCount FROM INFORMATION_SCHEMA.COLUMNS " +
|
||||
"WHERE TABLE_SCHEMA = '" + dbName + "' " +
|
||||
"AND TABLE_NAME = 'backpack_data' " +
|
||||
"AND COLUMN_NAME = 'uuid';"
|
||||
);
|
||||
ResultSet rsBackpackCol = backpackColCheck.resultSet();
|
||||
if (rsBackpackCol.next() && rsBackpackCol.getInt("colCount") == 0) {
|
||||
LOGGER.info("Altering backpack_data table to add missing 'uuid' column.");
|
||||
// Add the missing column and set it as primary key.
|
||||
JDBCsetUp.executeUpdate("ALTER TABLE " + dbName + ".backpack_data ADD COLUMN uuid CHAR(36) NOT NULL", 1);
|
||||
JDBCsetUp.executeUpdate("ALTER TABLE " + dbName + ".backpack_data ADD PRIMARY KEY (uuid)", 1);
|
||||
}
|
||||
rsBackpackCol.close();
|
||||
backpackColCheck.connection().close();
|
||||
addColumnIfNotExists("backpack_data", "uuid", "CHAR(36) NOT NULL", true);
|
||||
}
|
||||
|
||||
// Check and alter the 'advancements' column in player_data if necessary
|
||||
|
|
@ -170,8 +177,8 @@ public class PlayerSync {
|
|||
if (rsAdvCol.next()) {
|
||||
String dataType = rsAdvCol.getString("DATA_TYPE");
|
||||
if (!"mediumblob".equalsIgnoreCase(dataType)) {
|
||||
LOGGER.info("Altering player_data table to modify 'advancements' column to MEDIUMBLOB.");
|
||||
JDBCsetUp.executeUpdate("ALTER TABLE " + dbName + ".player_data MODIFY COLUMN advancements MEDIUMBLOB", 1);
|
||||
LOGGER.info("Altering player_data table to modify 'advancements' column from {} to MEDIUMBLOB.", dataType);
|
||||
JDBCsetUp.executeUpdateWithoutDatabase("ALTER TABLE " + dbName + ".player_data MODIFY COLUMN advancements MEDIUMBLOB");
|
||||
}
|
||||
}
|
||||
rsAdvCol.close();
|
||||
|
|
@ -185,4 +192,44 @@ public class PlayerSync {
|
|||
ChatSync.shutdown();
|
||||
}
|
||||
|
||||
private static void addColumnIfNotExists(String tableName, String columnName, String dataTypeDefaultNullness,
|
||||
boolean makePrimaryKey) throws SQLException {
|
||||
|
||||
// Making use of the AutoCloseable QueryResult here
|
||||
try (JDBCsetUp.QueryResult backpackColCheck = JDBCsetUp.executeQuery(
|
||||
"SELECT COUNT(*) AS colCount FROM INFORMATION_SCHEMA.COLUMNS " +
|
||||
"WHERE TABLE_SCHEMA = DATABASE()" +
|
||||
"AND TABLE_NAME = '" + tableName + "' " +
|
||||
"AND COLUMN_NAME = '" + columnName + "';")) {
|
||||
ResultSet rsBackpackCol = backpackColCheck.resultSet();
|
||||
|
||||
if (!rsBackpackCol.next()) {
|
||||
LOGGER.warn("Warning: Unable to check existence of colum {} in table {}.", columnName, tableName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rsBackpackCol.getInt("colCount") > 0) {
|
||||
LOGGER.debug("Column {} already exists. Skipping creation.", columnName);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LOGGER.info("ALTER {} table to add missing {} column.", tableName, columnName);
|
||||
// Add the missing column and set it as primary key.
|
||||
JDBCsetUp.executeUpdate(
|
||||
"ALTER TABLE %s ADD COLUMN %s %s",
|
||||
tableName, columnName, dataTypeDefaultNullness);
|
||||
|
||||
if (makePrimaryKey) {
|
||||
LOGGER.info("Altering {} table to add primary key on {}.", tableName, columnName);
|
||||
JDBCsetUp.executeUpdate(
|
||||
"ALTER TABLE %s ADD PRIMARY KEY (%s)",
|
||||
tableName, columnName);
|
||||
}
|
||||
}
|
||||
|
||||
private static void addColumnIfNotExists(String tableName, String columnName,
|
||||
String dataTypeDefaultNullness) throws SQLException {
|
||||
addColumnIfNotExists(tableName, columnName, dataTypeDefaultNullness, false);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,31 +1,32 @@
|
|||
package vip.fubuki.playersync.config;
|
||||
|
||||
|
||||
import net.neoforged.neoforge.common.ModConfigSpec;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import net.neoforged.neoforge.common.ModConfigSpec;
|
||||
|
||||
|
||||
public class JdbcConfig {
|
||||
public static ModConfigSpec COMMON_CONFIG;
|
||||
public static ModConfigSpec.ConfigValue<String> HOST;
|
||||
public static ModConfigSpec.IntValue PORT;
|
||||
public static ModConfigSpec.ConfigValue<String> USERNAME;
|
||||
public static ModConfigSpec.ConfigValue<String> PASSWORD;
|
||||
public static ModConfigSpec.ConfigValue<String> DATABASE_NAME;
|
||||
public static ModConfigSpec.ConfigValue<List<String>> SYNC_WORLD;
|
||||
public static ModConfigSpec.BooleanValue SYNC_ADVANCEMENTS;
|
||||
public static ModConfigSpec.BooleanValue USE_SSL;
|
||||
public static ModConfigSpec.BooleanValue SYNC_CHAT;
|
||||
public static ModConfigSpec.BooleanValue IS_CHAT_SERVER;
|
||||
public static final ModConfigSpec COMMON_CONFIG;
|
||||
public static final ModConfigSpec.ConfigValue<String> HOST;
|
||||
public static final ModConfigSpec.IntValue PORT;
|
||||
public static final ModConfigSpec.ConfigValue<String> USERNAME;
|
||||
public static final ModConfigSpec.ConfigValue<String> PASSWORD;
|
||||
public static final ModConfigSpec.ConfigValue<String> DATABASE_NAME;
|
||||
public static final ModConfigSpec.ConfigValue<List<String>> SYNC_WORLD;
|
||||
public static final ModConfigSpec.BooleanValue SYNC_ADVANCEMENTS;
|
||||
public static final ModConfigSpec.BooleanValue USE_SSL;
|
||||
public static final ModConfigSpec.BooleanValue SYNC_CHAT;
|
||||
public static final ModConfigSpec.BooleanValue IS_CHAT_SERVER;
|
||||
public static final ModConfigSpec.ConfigValue<String> ITEM_PLACEHOLDER_TITLE_OVERRIDE;
|
||||
public static final ModConfigSpec.ConfigValue<String> ITEM_PLACEHOLDER_DESCRIPTION_OVERRIDE;
|
||||
public static ModConfigSpec.ConfigValue<String> CHAT_SERVER_IP;
|
||||
public static ModConfigSpec.IntValue CHAT_SERVER_PORT;
|
||||
public static ModConfigSpec.BooleanValue USE_LEGACY_SERIALIZATION;
|
||||
public static final ModConfigSpec.ConfigValue<String> CHAT_SERVER_IP;
|
||||
public static final ModConfigSpec.IntValue CHAT_SERVER_PORT;
|
||||
public static final ModConfigSpec.BooleanValue USE_LEGACY_SERIALIZATION;
|
||||
|
||||
public static ModConfigSpec.ConfigValue<Integer> SERVER_ID;
|
||||
public static final ModConfigSpec.ConfigValue<Integer> SERVER_ID;
|
||||
|
||||
|
||||
static {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,10 @@ import net.minecraft.nbt.NbtUtils;
|
|||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.neoforged.fml.ModList;
|
||||
import top.theillusivec4.curios.api.CuriosApi;
|
||||
import top.theillusivec4.curios.api.type.capability.ICuriosItemHandler;
|
||||
import top.theillusivec4.curios.api.type.inventory.ICurioStacksHandler;
|
||||
import top.theillusivec4.curios.api.type.inventory.IDynamicStackHandler;
|
||||
import vip.fubuki.playersync.PlayerSync;
|
||||
import vip.fubuki.playersync.util.JDBCsetUp;
|
||||
import vip.fubuki.playersync.util.LocalJsonUtil;
|
||||
|
|
@ -14,16 +18,9 @@ import java.sql.ResultSet;
|
|||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import top.theillusivec4.curios.api.CuriosApi;
|
||||
import top.theillusivec4.curios.api.type.capability.ICuriosItemHandler;
|
||||
import top.theillusivec4.curios.api.type.inventory.ICurioStacksHandler;
|
||||
import top.theillusivec4.curios.api.type.inventory.IDynamicStackHandler;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import static vip.fubuki.playersync.sync.VanillaSync.deserializeString;
|
||||
|
||||
|
||||
public class ModsSupport {
|
||||
|
||||
|
|
@ -39,14 +36,8 @@ public class ModsSupport {
|
|||
ResultSet rs = qr.resultSet();
|
||||
if (rs.next()) {
|
||||
String curiosData = rs.getString("curios_item");
|
||||
if (curiosData.length() <= 2) {
|
||||
rs.close();
|
||||
qr.connection().close();
|
||||
return;
|
||||
}
|
||||
// Parse the stored data (assumes a simple Map.toString() format: "{key=value, key2=value2, ...}")
|
||||
Map<String, String> storedMap = LocalJsonUtil.StringToMap(curiosData);
|
||||
|
||||
// Clear current Curios slots to avoid conflicts.
|
||||
handlerOpt.ifPresent(handler -> handler.getCurios().forEach((slotType, stacksHandler) -> {
|
||||
// Use the dynamic stack handler to clear slots.
|
||||
|
|
@ -56,6 +47,12 @@ public class ModsSupport {
|
|||
}
|
||||
}));
|
||||
|
||||
if (curiosData.length() <= 2) {
|
||||
rs.close();
|
||||
qr.connection().close();
|
||||
return;
|
||||
}
|
||||
|
||||
// Restore each saved item.
|
||||
handlerOpt.ifPresent(handler -> {
|
||||
for (Map.Entry<String, String> entry : storedMap.entrySet()) {
|
||||
|
|
@ -73,9 +70,7 @@ public class ModsSupport {
|
|||
}
|
||||
String serialized = entry.getValue();
|
||||
try {
|
||||
String nbtString = VanillaSync.deserializeString(serialized);
|
||||
CompoundTag tag = NbtUtils.snbtToStructure(nbtString);
|
||||
ItemStack stack = ItemStack.of(tag);
|
||||
ItemStack stack = VanillaSync.deserializeAndCreatePlaceholderIfNeeded(serialized);
|
||||
if (handler.getCurios().containsKey(slotType)) {
|
||||
ICurioStacksHandler stacksHandler = handler.getCurios().get(slotType);
|
||||
IDynamicStackHandler dynStacks = stacksHandler.getStacks();
|
||||
|
|
@ -112,7 +107,7 @@ public class ModsSupport {
|
|||
ResultSet rsBackpack = qrBackpack.resultSet();
|
||||
if (rsBackpack.next()) {
|
||||
String serialized = rsBackpack.getString("backpack_nbt");
|
||||
String nbtString = deserializeString(serialized);
|
||||
String nbtString = VanillaSync.deserializeString(serialized);
|
||||
CompoundTag backpackNbt = NbtUtils.snbtToStructure(nbtString);
|
||||
// Update BackpackStorage with the retrieved NBT
|
||||
net.p3pp3rf1y.sophisticatedbackpacks.backpack.BackpackStorage.get().setBackpackContents(contentsUuid, backpackNbt);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
package vip.fubuki.playersync.sync;
|
||||
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.serialization.Dynamic;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.nbt.*;
|
||||
import net.minecraft.network.chat.Component;
|
||||
|
|
@ -10,6 +12,8 @@ import net.minecraft.resources.ResourceLocation;
|
|||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.PlayerAdvancements;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.util.datafix.DataFixers;
|
||||
import net.minecraft.util.datafix.fixes.References;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.effect.MobEffect;
|
||||
import net.minecraft.world.effect.MobEffectInstance;
|
||||
|
|
@ -247,7 +251,7 @@ public class VanillaSync {
|
|||
}
|
||||
|
||||
// deserialize item and potentially create placeholders
|
||||
private static ItemStack deserializeAndCreatePlaceholderIfNeeded(String serializedNbt)
|
||||
public static ItemStack deserializeAndCreatePlaceholderIfNeeded(String serializedNbt)
|
||||
throws CommandSyntaxException {
|
||||
if (serializedNbt == null || serializedNbt.isEmpty() || serializedNbt.equals("B64:e30=")) {
|
||||
// Check for empty NBT (Base64 encoded '{}')
|
||||
|
|
@ -255,7 +259,7 @@ public class VanillaSync {
|
|||
}
|
||||
|
||||
String nbtString = deserializeString(serializedNbt);
|
||||
CompoundTag compoundTag = NbtUtils.snbtToStructure(nbtString);
|
||||
CompoundTag compoundTag = snbtToFixedCompoundTag(nbtString);
|
||||
|
||||
if (compoundTag == null || compoundTag.isEmpty() || !compoundTag.contains("id", Tag.TAG_STRING)) {
|
||||
return ItemStack.EMPTY; // Invalid or empty tag
|
||||
|
|
@ -348,6 +352,23 @@ public class VanillaSync {
|
|||
return placeholder;
|
||||
}
|
||||
|
||||
public static CompoundTag snbtToFixedCompoundTag(String nbtString) throws CommandSyntaxException {
|
||||
CompoundTag parsedTag = TagParser.parseTag(nbtString);
|
||||
|
||||
int currentDataVersion = SharedConstants.getCurrentVersion().getDataVersion().getVersion();
|
||||
int snbtDataVersion = NbtUtils.getDataVersion(parsedTag, 500);
|
||||
|
||||
Dynamic<Tag> dynamicTagInput = new Dynamic<>(NbtOps.INSTANCE, parsedTag);
|
||||
|
||||
Dynamic<Tag> updatedDynamicTag = DataFixers.getDataFixer().update(
|
||||
References.ITEM_STACK,
|
||||
dynamicTagInput,
|
||||
snbtDataVersion,
|
||||
currentDataVersion);
|
||||
CompoundTag compoundTag = (CompoundTag) updatedDynamicTag.getValue();
|
||||
return compoundTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes a string from the database back into an NBT string.
|
||||
* Handles both the new Base64 format (prefixed with "B64:") and the old custom format.
|
||||
|
|
@ -455,6 +476,8 @@ public class VanillaSync {
|
|||
// Serialize the ItemStack to NBT
|
||||
CompoundTag compoundTag = new CompoundTag();
|
||||
itemStack.save(compoundTag);
|
||||
// Adding data version to allow newer version of Minecraft to properly update the itemstack from the db
|
||||
NbtUtils.addCurrentDataVersion(compoundTag);
|
||||
return compoundTag;
|
||||
}
|
||||
|
||||
|
|
@ -463,7 +486,7 @@ public class VanillaSync {
|
|||
PlayerSync.LOGGER.info("Storing data for player " + player_uuid + " (init=" + init + ")");
|
||||
|
||||
// Basic Attributes
|
||||
int XP = player.totalExperience;
|
||||
int XP = getTotalExperience(player);
|
||||
int score = player.getScore();
|
||||
int food_level = player.getFoodData().getFoodLevel();
|
||||
int health = (int) player.getHealth();
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
package vip.fubuki.playersync.util;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import org.slf4j.Logger;
|
||||
import vip.fubuki.playersync.config.JdbcConfig;
|
||||
|
||||
import java.sql.*;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
|
||||
public class JDBCsetUp {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
|
@ -45,7 +43,8 @@ public class JDBCsetUp {
|
|||
/**
|
||||
* Executes a query using a connection that includes the database.
|
||||
*/
|
||||
public static QueryResult executeQuery(String sql) throws SQLException {
|
||||
public static QueryResult executeQuery(String sqlFormatString, Object... args) throws SQLException {
|
||||
String sql = String.format(sqlFormatString, args);
|
||||
LOGGER.trace(sql);
|
||||
Connection connection = getConnection(); // With database selected (and "USE" already run)
|
||||
PreparedStatement queryStatement = connection.prepareStatement(sql);
|
||||
|
|
@ -54,28 +53,31 @@ public class JDBCsetUp {
|
|||
}
|
||||
|
||||
/**
|
||||
* Executes an update using a connection that includes the database.
|
||||
* Executes an update using a connection with or without the database within the JDBC URL
|
||||
*/
|
||||
public static void executeUpdate(String sql) throws SQLException {
|
||||
private static void executeUpdate(boolean selectDatabase, String sqlFormatString, Object... args) throws SQLException {
|
||||
String sql = String.format(sqlFormatString, args);
|
||||
LOGGER.trace(sql);
|
||||
try (Connection connection = getConnection()) { // With database selected
|
||||
try (Connection connection = getConnection(selectDatabase)) {
|
||||
try (PreparedStatement updateStatement = connection.prepareStatement(sql)) {
|
||||
updateStatement.executeUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes an update using a connection that includes the database in the JDBC URL
|
||||
*/
|
||||
public static void executeUpdate(String sqlFormatString, Object... args) throws SQLException {
|
||||
executeUpdate(true, sqlFormatString, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes an update using a connection that does NOT include a default database.
|
||||
* This method is used for commands like "CREATE DATABASE IF NOT EXISTS ..."
|
||||
*/
|
||||
public static void executeUpdate(String sql, int dummy) throws SQLException {
|
||||
LOGGER.trace(sql);
|
||||
try (Connection connection = getConnection(false)) { // Without default database
|
||||
try (PreparedStatement updateStatement = connection.prepareStatement(sql)) {
|
||||
updateStatement.executeUpdate();
|
||||
}
|
||||
}
|
||||
public static void executeUpdateWithoutDatabase(String sqlFormatString, Object... args) throws SQLException {
|
||||
executeUpdate(false, sqlFormatString, args);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -92,6 +94,24 @@ public class JDBCsetUp {
|
|||
}
|
||||
}
|
||||
|
||||
public record QueryResult(Connection connection, ResultSet resultSet) {
|
||||
public record QueryResult(Connection connection, ResultSet resultSet) implements AutoCloseable {
|
||||
@Override
|
||||
public void close() {
|
||||
if (resultSet != null) {
|
||||
try {
|
||||
resultSet.close();
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Error closing ResultSet", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (connection != null) {
|
||||
try {
|
||||
connection.close();
|
||||
} catch (SQLException e) {
|
||||
LOGGER.error("Error closing Connection", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,31 +2,44 @@ package vip.fubuki.playersync.util;
|
|||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class LocalJsonUtil {
|
||||
public static Map<String,String> StringToMap(String param) {
|
||||
Map<String,String> map = new HashMap<>();
|
||||
String s1 = param.substring(1,param.length()-1);
|
||||
String s2 = s1.trim();
|
||||
String[] split = s2.split(",");
|
||||
for (int i = split.length - 1; i >= 0; i--) {
|
||||
String trim = split[i].trim();
|
||||
String[] split1 = trim.split("=");
|
||||
map.put(split1[0],split1[1]);
|
||||
private static <K> Map<K, String> stringToGenericMap(String param, Function<String, K> keyParser) {
|
||||
Map<K, String> map = new HashMap<>();
|
||||
|
||||
// check if string is at least minimal json
|
||||
if (param == null || param.length() < 2 || param.equals("{}")) {
|
||||
return map;
|
||||
}
|
||||
|
||||
// extract string within outermost json brackets {}
|
||||
String s1 = param.substring(param.indexOf('{')+1, param.lastIndexOf('}')).trim();
|
||||
if (s1.isEmpty()) {
|
||||
return map;
|
||||
}
|
||||
|
||||
// split all json elements
|
||||
for (String split : s1.split(",")) {
|
||||
String trim = split.trim();
|
||||
|
||||
// only check for the first "=" as the values also contain additional "="
|
||||
int equalIndex = trim.indexOf('=');
|
||||
if (equalIndex < 0)
|
||||
continue;
|
||||
|
||||
String key = trim.substring(0, equalIndex);
|
||||
String value = trim.substring(equalIndex + 1);
|
||||
map.put(keyParser.apply(key), value);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public static Map<Integer,String> StringToEntryMap(String param) {
|
||||
Map<Integer,String> map = new HashMap<>();
|
||||
String s1 = param.substring(1,param.length()-1);
|
||||
String s2 = s1.trim();
|
||||
String[] split = s2.split(",");
|
||||
for (int i = split.length - 1; i >= 0; i--) {
|
||||
String trim = split[i].trim();
|
||||
String[] split1 = trim.split("=");
|
||||
map.put(Integer.parseInt(split1[0]),split1[1]);
|
||||
}
|
||||
return map;
|
||||
public static Map<String, String> StringToMap(String param) {
|
||||
return stringToGenericMap(param, Function.identity());
|
||||
}
|
||||
|
||||
public static Map<Integer, String> StringToEntryMap(String param) {
|
||||
return stringToGenericMap(param, Integer::parseInt);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user