Skip to content

Commit

Permalink
implemented asynchronous operations for commands, translation, and me…
Browse files Browse the repository at this point in the history
…ssage store
  • Loading branch information
DoggySazHi committed May 23, 2024
1 parent c6a91a2 commit ea35fd4
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextDecoration;
import net.kyori.adventure.text.serializer.json.JSONComponentSerializer;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
Expand All @@ -28,11 +29,12 @@
import java.util.UUID;

public class KosuzuLearnsEverything implements CommandExecutor {

private final Kosuzu kosuzu;
private final KosuzuRemembersEverything database;
private final KosuzuTranslatesEverything translator;

public KosuzuLearnsEverything(Kosuzu kosuzu) {
this.kosuzu = kosuzu;
database = kosuzu.database;
translator = new KosuzuTranslatesEverything(kosuzu);
}
Expand All @@ -45,17 +47,17 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command
}

if (args[0].equalsIgnoreCase("default")) {
changeUserLanguage(sender, args);
Bukkit.getScheduler().runTaskAsynchronously(kosuzu, () -> changeUserLanguage(sender, args));
return true;
}

if (args[0].equalsIgnoreCase("translate")) {
translateMessage(sender, args);
Bukkit.getScheduler().runTaskAsynchronously(kosuzu, () -> translateMessage(sender, args));
return true;
}

if (args[0].equalsIgnoreCase("auto")) {
setAuto(sender, args);
Bukkit.getScheduler().runTaskAsynchronously(kosuzu, () -> setAuto(sender, args));
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import static net.gensokyoreimagined.motoori.KosuzuDatabaseModels.*;

public class KosuzuRemembersEverything implements Closeable {
private final Kosuzu kosuzu;
private final YamlConfiguration translations;
private final BasicDataSource dataSource = new BasicDataSource();
private final FileConfiguration config;
Expand All @@ -49,6 +50,7 @@ public class KosuzuRemembersEverything implements Closeable {
private boolean isSqlite = false;

public KosuzuRemembersEverything(Kosuzu kosuzu) {
this.kosuzu = kosuzu;
config = kosuzu.config;
logger = kosuzu.getLogger();

Expand Down Expand Up @@ -219,6 +221,11 @@ private void loadLanguages() {
}
}

/**
* Change MySQL queries to be compatible with SQLite
* @param sql The MySQL-compatible SQL query
* @return The SQLite-compatible SQL query
*/
private String s(String sql) {
if (!isSqlite) {
return sql;
Expand Down Expand Up @@ -368,7 +375,7 @@ public Collection<Language> getLanguages() {

public Translation getTranslation(@NotNull UUID message, @NotNull UUID user) {
try (var connection = getConnection()) {
try (var statement = connection.prepareStatement("SELECT user_message.json_msg, message.uuid AS message_id, message.language, message.text, message_translation.text AS translation, `user`.`default_language` FROM `user_message` LEFT JOIN `message` ON message.uuid = user_message.message_id LEFT JOIN `user` ON `user`.`uuid` = ? LEFT JOIN `message_translation` ON message_translation.message_id = message.uuid AND user.default_language = message_translation.language WHERE `user_message`.`uuid` = ?;")) {
try (var statement = connection.prepareStatement("SELECT user_message.json_msg, message.uuid AS message_id, message.language, message.text, message_translation.text AS translation, `user`.`default_language` FROM `user_message_lookup` INNER JOIN `user_message` ON user_message.uuid = user_message_lookup.user_message_id LEFT JOIN `message` ON message.uuid = user_message.message_id LEFT JOIN `user` ON `user`.`uuid` = ? LEFT JOIN `message_translation` ON message_translation.message_id = message.uuid AND user.default_language = message_translation.language WHERE `user_message_lookup`.`uuid` = ?;")) {
statement.setString(1, user.toString());
statement.setString(2, message.toString());
try (var result = statement.executeQuery()) {
Expand Down Expand Up @@ -419,21 +426,35 @@ public void addTranslation(@NotNull UUID message, @NotNull String translation, @
.build(
new CacheLoader<>() {
public @NotNull UUID load(@NotNull Message json) {
return addMessageSQL(json.getJSON(), json.getMessage());
return addMessageSQLWrapper(json.getJSON(), json.getMessage());
}
});

public UUID addMessage(@NotNull String json, @NotNull String message) {
return messageCache.getUnchecked(new Message(message, json));
}

private @NotNull UUID addMessageSQL(@NotNull String json, @NotNull String message) {
UUID uuid = null;
/**
* OK, so second issue: what if SQL is slow? We can't wait for SQL to get a UUID, so we generate one ahead of time.
* So, we've created a new table called `user_message_lookup` that links the eagerly generated UUID to the actual UUID.
* Note that it's possible that a person tries to translate a message before it's been added to the database (async).
*/
private @NotNull UUID addMessageSQLWrapper(@NotNull String json, @NotNull String message) {
UUID uuid = UUID.randomUUID();

// Invoke addMessageSQL asynchronously
kosuzu.getServer().getScheduler().runTaskAsynchronously(kosuzu, () -> {
addMessageSQL(uuid, json, message);
});

return uuid;
}

private void addMessageSQL(@NotNull UUID lookupUUID, @NotNull String json, @NotNull String message) {
try (var connection = getConnection()) {
// TODO this should be moved into a stored procedure (dropping support for SQLite)
UUID messageUUID = null;

// Store the message contents (plain text) in the database
try (var statement = connection.prepareStatement("SELECT uuid FROM `message` WHERE `text` = ?")) {
statement.setString(1, message);
try (var data = statement.executeQuery()) {
Expand All @@ -452,6 +473,8 @@ public UUID addMessage(@NotNull String json, @NotNull String message) {
}
}

UUID uuid = null;
// Store the JSON Minecraft message in the database
try (var statement = connection.prepareStatement("SELECT `uuid` FROM `user_message` WHERE `json_msg` = ?")) {
statement.setString(1, json);
try (var data = statement.executeQuery()) {
Expand All @@ -471,12 +494,17 @@ public UUID addMessage(@NotNull String json, @NotNull String message) {
statement.execute();
}
}

// Finally, store the lookup UUID
try (var statement = connection.prepareStatement("INSERT INTO `user_message_lookup` (`uuid`, `user_message_id`) VALUES (?, ?)")) {
statement.setString(1, lookupUUID.toString());
statement.setString(2, uuid.toString());
statement.execute();
}
} catch (SQLException e) {
logger.severe("Failed to add message!");
logger.severe(e.getMessage());
}

return uuid == null ? UUID.randomUUID() : uuid;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ private void onChatDecorateEarliest(AsyncChatDecorateEvent event) {
if (player == null) return;

// filter early to adjust for following decorators
event.result(parser.removeUnwantedSyntax(event.originalMessage()));
var message = event.originalMessage();
message = parser.removeUnwantedSyntax(message);
// Don't make links clickable twice
event.result(message);
}

@EventHandler(priority = EventPriority.HIGHEST)
Expand All @@ -58,7 +61,9 @@ private void onChatDecorateLatest(AsyncChatDecorateEvent event) {
if (player == null) return;

// retrieve original filtered message
var message = parser.removeUnwantedSyntax(event.originalMessage());
var message = event.originalMessage();
message = parser.removeUnwantedSyntax(message);
message = parser.makeLinksClickable(message);

var json = JSONComponentSerializer.json().serialize(event.result());
var uuid = database.addMessage(json, PlainTextComponentSerializer.plainText().serialize(message));
Expand Down

0 comments on commit ea35fd4

Please sign in to comment.