mirror of
https://github.com/Jikoo/OpenInv.git
synced 2026-11-04 08:42:05 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ee2546efd | ||
|
|
0e6acdff36 | ||
|
|
3d4bed04d5 | ||
|
|
e885bf9a65 | ||
|
|
e15c566b82 | ||
|
|
c1ffd09d50 | ||
|
|
bd5e47760e | ||
|
|
2fe0322570 | ||
|
|
767476a759 | ||
|
|
dc8e36d11f | ||
|
|
79b2a97c9e | ||
|
|
81eb60f628 | ||
|
|
e39f092f14 | ||
|
|
4816737b90 |
No files matched your search
@@ -0,0 +1,26 @@
|
||||
name: Auto-merge Dependabot PRs
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: [ "OpenInv CI" ]
|
||||
types: [ completed ]
|
||||
|
||||
jobs:
|
||||
merge-dependabot:
|
||||
if: "github.actor == 'dependabot[bot]'
|
||||
&& github.event.workflow_run.event == 'pull_request'
|
||||
&& github.event.workflow_run.conclusion == 'success'"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Approve
|
||||
uses: hmarr/auto-approve-action@v3.1.0
|
||||
with:
|
||||
github-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
pull-request-number: "${{ github.event.workflow_run.event.pull_request.id }}"
|
||||
- name: Merge
|
||||
uses: pascalgn/automerge-action@v0.15.6
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
MERGE_LABELS: "dependencies,java"
|
||||
MERGE_METHOD: "squash"
|
||||
PULL_REQUEST: "${{ github.event.workflow_run.event.pull_request.id }}"
|
||||
@@ -2,7 +2,7 @@ name: OpenInv CI
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request_target:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -36,54 +36,3 @@ jobs:
|
||||
with:
|
||||
name: api
|
||||
path: ./api/target/openinvapi*.jar
|
||||
|
||||
merge-dependabot:
|
||||
name: Auto-merge Dependabot PRs
|
||||
needs: [ build ]
|
||||
if: "github.event.name == 'pull_request_target'
|
||||
&& github.actor == 'dependabot[bot]'
|
||||
&& contains( github.event.pull_request.labels.*.name, 'java')"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Approve
|
||||
uses: hmarr/auto-approve-action@v3.1.0
|
||||
with:
|
||||
github-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
- name: Merge
|
||||
uses: pascalgn/automerge-action@v0.15.5
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
MERGE_LABELS: "dependencies"
|
||||
MERGE_METHOD: "squash"
|
||||
|
||||
release:
|
||||
name: Create Github Release
|
||||
needs: [ build ]
|
||||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# Fetch all history - used to assemble changelog.
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set Release Variables
|
||||
run: bash ./scripts/set_release_env.sh
|
||||
|
||||
- name: Download Artifact
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: dist
|
||||
path: dist
|
||||
|
||||
- name: Create Release
|
||||
id: create-release
|
||||
uses: softprops/action-gh-release@v0.1.15
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
name: ${{ env.VERSIONED_NAME }}
|
||||
body: ${{ env.GENERATED_CHANGELOG }}
|
||||
draft: true
|
||||
prerelease: false
|
||||
files: ./dist/OpenInv.jar
|
||||
@@ -0,0 +1,44 @@
|
||||
name: Draft Github Release
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: [ "OpenInv CI" ]
|
||||
types: [ completed ]
|
||||
|
||||
jobs:
|
||||
draft_release:
|
||||
if: "github.event.workflow_run.event == 'push'
|
||||
&& github.event.workflow_run.conclusion == 'success'
|
||||
&& startsWith(github.event.workflow_run.event.push.ref, 'refs/tags/')"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# Fetch all history - used to assemble changelog.
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set Release Variables
|
||||
run: bash ./scripts/set_release_env.sh
|
||||
|
||||
- name: Download Artifact
|
||||
# Unfortunately actions/download-artifact cannot fetch from other workflow runs.
|
||||
uses: dawidd6/action-download-artifact@v2.26.0
|
||||
with:
|
||||
name: dist
|
||||
path: dist
|
||||
run_id: "${{ github.event.workflow_run.id }}"
|
||||
run_number: "${{ github.event.workflow_run.run_number }}"
|
||||
# Searching for a specific run ID that we know was successful, unset 'success' default.
|
||||
workflow_conclusion: ""
|
||||
|
||||
- name: Create Release
|
||||
id: create-release
|
||||
uses: softprops/action-gh-release@v0.1.15
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
name: ${{ env.VERSIONED_NAME }}
|
||||
body: ${{ env.GENERATED_CHANGELOG }}
|
||||
draft: true
|
||||
prerelease: false
|
||||
files: ./dist/OpenInv.jar
|
||||
File renamed without changes.
+1
-1
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>openinvparent</artifactId>
|
||||
<groupId>com.lishid</groupId>
|
||||
<version>4.2.2</version>
|
||||
<version>4.3.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>openinvapi</artifactId>
|
||||
|
||||
@@ -22,9 +22,7 @@ import com.lishid.openinv.internal.ISpecialEnderChest;
|
||||
import com.lishid.openinv.internal.ISpecialInventory;
|
||||
import com.lishid.openinv.internal.ISpecialPlayerInventory;
|
||||
import com.lishid.openinv.util.InventoryAccess;
|
||||
import com.lishid.openinv.util.StringMetric;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
@@ -65,6 +63,15 @@ public interface IOpenInv {
|
||||
*/
|
||||
boolean disableOfflineAccess();
|
||||
|
||||
/**
|
||||
* Check the configuration value for whether OpenInv uses history for opening commands. If false, OpenInv will use
|
||||
* the previous parameterized search when no parameters are provided.
|
||||
*
|
||||
* @return false unless configured otherwise
|
||||
* @since 4.3.0
|
||||
*/
|
||||
boolean noArgsOpensSelf();
|
||||
|
||||
/**
|
||||
* Get the active {@link IAnySilentContainer} implementation.
|
||||
*
|
||||
@@ -219,74 +226,7 @@ public interface IOpenInv {
|
||||
* @param name the string to match
|
||||
* @return the user with the closest matching name
|
||||
*/
|
||||
default @Nullable OfflinePlayer matchPlayer(@NotNull String name) {
|
||||
|
||||
// Warn if called on the main thread - if we resort to searching offline players, this may take several seconds.
|
||||
if (Bukkit.getServer().isPrimaryThread()) {
|
||||
this.getLogger().warning("Call to OpenInv#matchPlayer made on the main thread!");
|
||||
this.getLogger().warning("This can cause the server to hang, potentially severely.");
|
||||
this.getLogger().log(Level.WARNING, "Current stack trace", new Throwable("Current stack trace"));
|
||||
}
|
||||
|
||||
OfflinePlayer player;
|
||||
|
||||
try {
|
||||
UUID uuid = UUID.fromString(name);
|
||||
player = Bukkit.getOfflinePlayer(uuid);
|
||||
// Ensure player is an existing player.
|
||||
if (player.hasPlayedBefore() || player.isOnline()) {
|
||||
return player;
|
||||
}
|
||||
// Return null otherwise.
|
||||
return null;
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
// Not a UUID
|
||||
}
|
||||
|
||||
// Exact online match first.
|
||||
player = Bukkit.getServer().getPlayerExact(name);
|
||||
|
||||
if (player != null) {
|
||||
return player;
|
||||
}
|
||||
|
||||
// Exact offline match second - ensure offline access works when matchable users are online.
|
||||
player = Bukkit.getServer().getOfflinePlayer(name);
|
||||
|
||||
if (player.hasPlayedBefore()) {
|
||||
return player;
|
||||
}
|
||||
|
||||
// Inexact online match.
|
||||
player = Bukkit.getServer().getPlayer(name);
|
||||
|
||||
if (player != null) {
|
||||
return player;
|
||||
}
|
||||
|
||||
// Finally, inexact offline match.
|
||||
float bestMatch = 0;
|
||||
for (OfflinePlayer offline : Bukkit.getServer().getOfflinePlayers()) {
|
||||
if (offline.getName() == null) {
|
||||
// Loaded by UUID only, name has never been looked up.
|
||||
continue;
|
||||
}
|
||||
|
||||
float currentMatch = StringMetric.compareJaroWinkler(name, offline.getName());
|
||||
|
||||
if (currentMatch == 1.0F) {
|
||||
return offline;
|
||||
}
|
||||
|
||||
if (currentMatch > bestMatch) {
|
||||
bestMatch = currentMatch;
|
||||
player = offline;
|
||||
}
|
||||
}
|
||||
|
||||
// Only null if no players have played ever, otherwise even the worst match will do.
|
||||
return player;
|
||||
}
|
||||
@Nullable OfflinePlayer matchPlayer(@NotNull String name);
|
||||
|
||||
/**
|
||||
* @deprecated OpenInv uses action bar chat for notifications. Whether they show is based on language settings.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2022 lishid. All rights reserved.
|
||||
* Copyright (C) 2011-2023 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package com.lishid.openinv.internal;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Barrel;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
@@ -148,13 +147,20 @@ public interface IAnySilentContainer {
|
||||
* @return true if the type is a supported container
|
||||
*/
|
||||
default boolean isAnySilentContainer(@NotNull Block block) {
|
||||
if (block.getType() == Material.ENDER_CHEST) {
|
||||
return true;
|
||||
}
|
||||
BlockState state = block.getState();
|
||||
return state instanceof org.bukkit.block.Chest
|
||||
|| state instanceof org.bukkit.block.ShulkerBox
|
||||
|| state instanceof org.bukkit.block.Barrel;
|
||||
return isAnySilentContainer(block.getState());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given {@link BlockState} is a container which can be unblocked or silenced.
|
||||
*
|
||||
* @param blockState the potential container
|
||||
* @return true if the type is a supported container
|
||||
*/
|
||||
default boolean isAnySilentContainer(@NotNull BlockState blockState) {
|
||||
return blockState instanceof org.bukkit.block.EnderChest
|
||||
|| blockState instanceof org.bukkit.block.Chest
|
||||
|| blockState instanceof org.bukkit.block.ShulkerBox
|
||||
|| blockState instanceof org.bukkit.block.Barrel;
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvparent</artifactId>
|
||||
<version>4.2.2</version>
|
||||
<version>4.3.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>openinvassembly</artifactId>
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<artifactId>openinvparent</artifactId>
|
||||
<groupId>com.lishid</groupId>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
<version>4.2.2</version>
|
||||
<version>4.3.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>openinvadapter1_18_R2</artifactId>
|
||||
|
||||
+3
-14
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2022 lishid. All rights reserved.
|
||||
* Copyright (C) 2011-2023 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -50,12 +50,12 @@ import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Statistic;
|
||||
import org.bukkit.block.ShulkerBox;
|
||||
import org.bukkit.craftbukkit.v1_18_R2.CraftWorld;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -231,21 +231,10 @@ public class AnySilentContainer implements IAnySilentContainer {
|
||||
|
||||
@Override
|
||||
public void deactivateContainer(@NotNull final Player bukkitPlayer) {
|
||||
if (this.serverPlayerGameModeGameType == null) {
|
||||
if (this.serverPlayerGameModeGameType == null || bukkitPlayer.getGameMode() == GameMode.SPECTATOR) {
|
||||
return;
|
||||
}
|
||||
|
||||
InventoryView view = bukkitPlayer.getOpenInventory();
|
||||
switch (view.getType()) {
|
||||
case CHEST:
|
||||
case ENDER_CHEST:
|
||||
case SHULKER_BOX:
|
||||
case BARREL:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer);
|
||||
|
||||
// Force game mode change without informing plugins or players.
|
||||
|
||||
+3
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2022 lishid. All rights reserved.
|
||||
* Copyright (C) 2011-2023 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -37,7 +37,7 @@ public class OpenPlayer extends CraftPlayer {
|
||||
// See CraftPlayer#loadData
|
||||
CompoundTag loaded = this.server.getHandle().playerIo.load(this.getHandle());
|
||||
if (loaded != null) {
|
||||
readExtraData(loaded);
|
||||
getHandle().readAdditionalSaveData(loaded);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,6 @@ public class OpenPlayer extends CraftPlayer {
|
||||
PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo;
|
||||
|
||||
CompoundTag playerData = player.saveWithoutId(new CompoundTag());
|
||||
setExtraData(playerData);
|
||||
|
||||
if (!isOnline()) {
|
||||
// Special case: save old vehicle data
|
||||
@@ -67,7 +66,7 @@ public class OpenPlayer extends CraftPlayer {
|
||||
File file2 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat_old");
|
||||
Util.safeReplaceFile(file1, file, file2);
|
||||
} catch (Exception e) {
|
||||
LogManager.getLogger().warn("Failed to save player data for {}: {}", player.getName().getString(), e.getMessage());
|
||||
LogManager.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+20
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2022 lishid. All rights reserved.
|
||||
* Copyright (C) 2011-2023 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -23,6 +23,7 @@ import com.lishid.openinv.internal.OpenInventoryView;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.logging.Logger;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.chat.TextComponent;
|
||||
import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
@@ -108,14 +109,25 @@ public class PlayerDataManager implements IPlayerDataManager {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// Get the bukkit entity
|
||||
Player target = entity.getBukkitEntity();
|
||||
if (target != null) {
|
||||
// Load data
|
||||
target.loadData();
|
||||
// Load data. This also reads basic data into the player.
|
||||
// See CraftPlayer#loadData
|
||||
CompoundTag loadedData = server.getPlayerList().playerIo.load(entity);
|
||||
|
||||
if (loadedData == null) {
|
||||
// Exceptions with loading are logged by Mojang.
|
||||
return null;
|
||||
}
|
||||
// Return the entity
|
||||
return target;
|
||||
|
||||
// Also read "extra" data.
|
||||
entity.readAdditionalSaveData(loadedData);
|
||||
|
||||
if (entity.level == null) {
|
||||
// Paper: Move player to spawn
|
||||
entity.spawnIn(null);
|
||||
}
|
||||
|
||||
// Return the Bukkit entity.
|
||||
return entity.getBukkitEntity();
|
||||
}
|
||||
|
||||
void injectPlayer(ServerPlayer player) throws IllegalAccessException {
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<artifactId>openinvparent</artifactId>
|
||||
<groupId>com.lishid</groupId>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
<version>4.2.2</version>
|
||||
<version>4.3.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>openinvadapter1_19_R2</artifactId>
|
||||
|
||||
+3
-14
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2022 lishid. All rights reserved.
|
||||
* Copyright (C) 2011-2023 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -50,12 +50,12 @@ import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Statistic;
|
||||
import org.bukkit.block.ShulkerBox;
|
||||
import org.bukkit.craftbukkit.v1_19_R2.CraftWorld;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -227,21 +227,10 @@ public class AnySilentContainer implements IAnySilentContainer {
|
||||
|
||||
@Override
|
||||
public void deactivateContainer(@NotNull final Player bukkitPlayer) {
|
||||
if (this.serverPlayerGameModeGameType == null) {
|
||||
if (this.serverPlayerGameModeGameType == null || bukkitPlayer.getGameMode() == GameMode.SPECTATOR) {
|
||||
return;
|
||||
}
|
||||
|
||||
InventoryView view = bukkitPlayer.getOpenInventory();
|
||||
switch (view.getType()) {
|
||||
case CHEST:
|
||||
case ENDER_CHEST:
|
||||
case SHULKER_BOX:
|
||||
case BARREL:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer);
|
||||
|
||||
// Force game mode change without informing plugins or players.
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2022 lishid. All rights reserved.
|
||||
* Copyright (C) 2011-2023 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -37,7 +37,7 @@ public class OpenPlayer extends CraftPlayer {
|
||||
// See CraftPlayer#loadData
|
||||
CompoundTag loaded = this.server.getHandle().playerIo.load(this.getHandle());
|
||||
if (loaded != null) {
|
||||
readExtraData(loaded);
|
||||
getHandle().readAdditionalSaveData(loaded);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+23
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2022 lishid. All rights reserved.
|
||||
* Copyright (C) 2011-2023 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -23,6 +23,7 @@ import com.lishid.openinv.internal.OpenInventoryView;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.logging.Logger;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
@@ -100,20 +101,34 @@ public class PlayerDataManager implements IPlayerDataManager {
|
||||
|
||||
ServerPlayer entity = new ServerPlayer(server, worldServer, profile);
|
||||
|
||||
// Stop listening for advancement progression - if this is not cleaned up, loading causes a memory leak.
|
||||
entity.getAdvancements().stopListening();
|
||||
|
||||
try {
|
||||
injectPlayer(entity);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// Get the bukkit entity
|
||||
Player target = entity.getBukkitEntity();
|
||||
if (target != null) {
|
||||
// Load data
|
||||
target.loadData();
|
||||
// Load data. This also reads basic data into the player.
|
||||
// See CraftPlayer#loadData
|
||||
CompoundTag loadedData = server.getPlayerList().playerIo.load(entity);
|
||||
|
||||
if (loadedData == null) {
|
||||
// Exceptions with loading are logged by Mojang.
|
||||
return null;
|
||||
}
|
||||
// Return the entity
|
||||
return target;
|
||||
|
||||
// Also read "extra" data.
|
||||
entity.readAdditionalSaveData(loadedData);
|
||||
|
||||
if (entity.level == null) {
|
||||
// Paper: Move player to spawn
|
||||
entity.spawnIn(null);
|
||||
}
|
||||
|
||||
// Return the Bukkit entity.
|
||||
return entity.getBukkitEntity();
|
||||
}
|
||||
|
||||
void injectPlayer(ServerPlayer player) throws IllegalAccessException {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2011-2022 lishid. All rights reserved.
|
||||
~ Copyright (C) 2011-2023 lishid. All rights reserved.
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
@@ -23,16 +23,16 @@
|
||||
<artifactId>openinvparent</artifactId>
|
||||
<groupId>com.lishid</groupId>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
<version>4.2.2</version>
|
||||
<version>4.3.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>openinvadapter1_19_R1</artifactId>
|
||||
<name>OpenInvAdapter1_19_R1</name>
|
||||
<artifactId>openinvadapter1_19_R3</artifactId>
|
||||
<name>OpenInvAdapter1_19_R3</name>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<spigot.version>1.19.1-R0.1-SNAPSHOT</spigot.version>
|
||||
<spigot.version>1.19.4-R0.1-SNAPSHOT</spigot.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
+5
-18
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2022 lishid. All rights reserved.
|
||||
* Copyright (C) 2011-2023 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -14,7 +14,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.internal.v1_19_R1;
|
||||
package com.lishid.openinv.internal.v1_19_R3;
|
||||
|
||||
import com.lishid.openinv.OpenInv;
|
||||
import com.lishid.openinv.internal.IAnySilentContainer;
|
||||
@@ -50,12 +50,12 @@ import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Statistic;
|
||||
import org.bukkit.block.ShulkerBox;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.CraftWorld;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -66,8 +66,6 @@ public class AnySilentContainer implements IAnySilentContainer {
|
||||
public AnySilentContainer() {
|
||||
try {
|
||||
try {
|
||||
// IDE warns about field not existing, but SpecialSource does not remap strings used in reflection.
|
||||
// The warning is not suppressed as a reminder that it must manually be checked on updates.
|
||||
this.serverPlayerGameModeGameType = ServerPlayerGameMode.class.getDeclaredField("b");
|
||||
this.serverPlayerGameModeGameType.setAccessible(true);
|
||||
} catch (NoSuchFieldException e) {
|
||||
@@ -231,21 +229,10 @@ public class AnySilentContainer implements IAnySilentContainer {
|
||||
|
||||
@Override
|
||||
public void deactivateContainer(@NotNull final Player bukkitPlayer) {
|
||||
if (this.serverPlayerGameModeGameType == null) {
|
||||
if (this.serverPlayerGameModeGameType == null || bukkitPlayer.getGameMode() == GameMode.SPECTATOR) {
|
||||
return;
|
||||
}
|
||||
|
||||
InventoryView view = bukkitPlayer.getOpenInventory();
|
||||
switch (view.getType()) {
|
||||
case CHEST:
|
||||
case ENDER_CHEST:
|
||||
case SHULKER_BOX:
|
||||
case BARREL:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer);
|
||||
|
||||
// Force game mode change without informing plugins or players.
|
||||
+6
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2022 lishid. All rights reserved.
|
||||
* Copyright (C) 2011-2023 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -14,7 +14,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.internal.v1_19_R1;
|
||||
package com.lishid.openinv.internal.v1_19_R3;
|
||||
|
||||
import java.io.File;
|
||||
import net.minecraft.Util;
|
||||
@@ -23,8 +23,8 @@ import net.minecraft.nbt.NbtIo;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.level.storage.PlayerDataStorage;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer;
|
||||
|
||||
public class OpenPlayer extends CraftPlayer {
|
||||
|
||||
@@ -37,7 +37,7 @@ public class OpenPlayer extends CraftPlayer {
|
||||
// See CraftPlayer#loadData
|
||||
CompoundTag loaded = this.server.getHandle().playerIo.load(this.getHandle());
|
||||
if (loaded != null) {
|
||||
readExtraData(loaded);
|
||||
getHandle().readAdditionalSaveData(loaded);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ public class OpenPlayer extends CraftPlayer {
|
||||
File file2 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat_old");
|
||||
Util.safeReplaceFile(file1, file, file2);
|
||||
} catch (Exception e) {
|
||||
LogManager.getLogger().warn("Failed to save player data for {}: {}", player.getName().getString(), e.getMessage());
|
||||
LogManager.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
+26
-14
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2022 lishid. All rights reserved.
|
||||
* Copyright (C) 2011-2023 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -14,7 +14,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.internal.v1_19_R1;
|
||||
package com.lishid.openinv.internal.v1_19_R3;
|
||||
|
||||
import com.lishid.openinv.OpenInv;
|
||||
import com.lishid.openinv.internal.IPlayerDataManager;
|
||||
@@ -23,6 +23,7 @@ import com.lishid.openinv.internal.OpenInventoryView;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.logging.Logger;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
@@ -35,10 +36,10 @@ import net.minecraft.world.level.Level;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.event.CraftEventFactory;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftContainer;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.event.CraftEventFactory;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftContainer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -98,7 +99,7 @@ public class PlayerDataManager implements IPlayerDataManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
ServerPlayer entity = new ServerPlayer(server, worldServer, profile, null);
|
||||
ServerPlayer entity = new ServerPlayer(server, worldServer, profile);
|
||||
|
||||
// Stop listening for advancement progression - if this is not cleaned up, loading causes a memory leak.
|
||||
entity.getAdvancements().stopListening();
|
||||
@@ -109,14 +110,25 @@ public class PlayerDataManager implements IPlayerDataManager {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// Get the bukkit entity
|
||||
Player target = entity.getBukkitEntity();
|
||||
if (target != null) {
|
||||
// Load data
|
||||
target.loadData();
|
||||
// Load data. This also reads basic data into the player.
|
||||
// See CraftPlayer#loadData
|
||||
CompoundTag loadedData = server.getPlayerList().playerIo.load(entity);
|
||||
|
||||
if (loadedData == null) {
|
||||
// Exceptions with loading are logged by Mojang.
|
||||
return null;
|
||||
}
|
||||
// Return the entity
|
||||
return target;
|
||||
|
||||
// Also read "extra" data.
|
||||
entity.readAdditionalSaveData(loadedData);
|
||||
|
||||
if (entity.level == null) {
|
||||
// Paper: Move player to spawn
|
||||
entity.spawnIn(null);
|
||||
}
|
||||
|
||||
// Return the Bukkit entity.
|
||||
return entity.getBukkitEntity();
|
||||
}
|
||||
|
||||
void injectPlayer(ServerPlayer player) throws IllegalAccessException {
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2022 lishid. All rights reserved.
|
||||
* Copyright (C) 2011-2023 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -14,7 +14,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.internal.v1_19_R1;
|
||||
package com.lishid.openinv.internal.v1_19_R3;
|
||||
|
||||
import com.lishid.openinv.internal.ISpecialEnderChest;
|
||||
import java.util.List;
|
||||
@@ -33,8 +33,8 @@ import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.entity.EnderChestBlockEntity;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.entity.CraftHumanEntity;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventory;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.entity.CraftHumanEntity;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventory;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
+6
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2022 lishid. All rights reserved.
|
||||
* Copyright (C) 2011-2023 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -14,7 +14,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.internal.v1_19_R1;
|
||||
package com.lishid.openinv.internal.v1_19_R3;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.lishid.openinv.internal.ISpecialPlayerInventory;
|
||||
@@ -33,6 +33,7 @@ import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.tags.DamageTypeTags;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.ContainerHelper;
|
||||
@@ -46,8 +47,8 @@ import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.entity.CraftHumanEntity;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftInventory;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.entity.CraftHumanEntity;
|
||||
import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventory;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -719,7 +720,7 @@ public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerI
|
||||
|
||||
for (int index : armorIndices) {
|
||||
ItemStack itemstack = this.armor.get(index);
|
||||
if ((!damagesource.isFire() || !itemstack.getItem().isFireResistant()) && itemstack.getItem() instanceof ArmorItem) {
|
||||
if ((!damagesource.is(DamageTypeTags.IS_FIRE) || !itemstack.getItem().isFireResistant()) && itemstack.getItem() instanceof ArmorItem) {
|
||||
itemstack.hurtAndBreak((int) damage, this.player, localPlayer -> localPlayer.broadcastBreakEvent(EquipmentSlot.byTypeAndIndex(EquipmentSlot.Type.ARMOR, index)));
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -21,7 +21,7 @@
|
||||
<parent>
|
||||
<artifactId>openinvparent</artifactId>
|
||||
<groupId>com.lishid</groupId>
|
||||
<version>4.2.2</version>
|
||||
<version>4.3.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>openinvplugincore</artifactId>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2022 lishid. All rights reserved.
|
||||
* Copyright (C) 2011-2023 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -64,6 +64,7 @@ class InternalAccessor {
|
||||
case "v1_8_R3", "v1_15_R1", "v1_16_R2" -> "https://github.com/lishid/OpenInv/releases/tag/4.1.5";
|
||||
case "v1_16_R3" -> "https://github.com/Jikoo/OpenInv/releases/tag/4.1.8";
|
||||
case "v1_17_R1", "v1_18_R1" -> "https://github.com/Jikoo/OpenInv/releases/tag/4.1.10";
|
||||
case "v1_19_R1" -> "https://github.com/Jikoo/OpenInv/releases/tag/4.2.2";
|
||||
default -> "https://github.com/Jikoo/OpenInv/releases";
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2022 lishid. All rights reserved.
|
||||
* Copyright (C) 2011-2023 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -24,6 +24,7 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.block.Container;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
@@ -53,7 +54,9 @@ record InventoryListener(OpenInv plugin) implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.plugin.getSilentContainerStatus(player)) {
|
||||
if (this.plugin.getSilentContainerStatus(player)
|
||||
&& event.getInventory().getHolder() instanceof Container container
|
||||
&& this.plugin.getAnySilentContainer().isAnySilentContainer(container)) {
|
||||
this.plugin.getAnySilentContainer().deactivateContainer(player);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.lishid.openinv;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.lishid.openinv.commands.ContainerSettingCommand;
|
||||
import com.lishid.openinv.commands.OpenInvCommand;
|
||||
import com.lishid.openinv.commands.SearchContainerCommand;
|
||||
@@ -26,9 +28,12 @@ import com.lishid.openinv.internal.ISpecialEnderChest;
|
||||
import com.lishid.openinv.internal.ISpecialInventory;
|
||||
import com.lishid.openinv.internal.ISpecialPlayerInventory;
|
||||
import com.lishid.openinv.util.ConfigUpdater;
|
||||
import com.lishid.openinv.util.LanguageManager;
|
||||
import com.lishid.openinv.util.Permissions;
|
||||
import com.lishid.openinv.util.StringMetric;
|
||||
import com.lishid.openinv.util.lang.LanguageManager;
|
||||
import com.lishid.openinv.util.lang.Replacement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
@@ -37,6 +42,7 @@ import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Stream;
|
||||
import net.md_5.bungee.api.ChatMessageType;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
@@ -52,6 +58,7 @@ import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.bukkit.profile.PlayerProfile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -62,6 +69,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
*/
|
||||
public class OpenInv extends JavaPlugin implements IOpenInv {
|
||||
|
||||
private final Cache<String, PlayerProfile> offlineLookUpCache = CacheBuilder.newBuilder().maximumSize(10).build();
|
||||
private final Map<UUID, ISpecialPlayerInventory> inventories = new ConcurrentHashMap<>();
|
||||
private final Map<UUID, ISpecialEnderChest> enderChests = new ConcurrentHashMap<>();
|
||||
|
||||
@@ -190,6 +198,11 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
|
||||
return this.getConfig().getBoolean("settings.disable-offline-access", false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean noArgsOpensSelf() {
|
||||
return this.getConfig().getBoolean("settings.command.open.no-args-opens-self", false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull IAnySilentContainer getAnySilentContainer() {
|
||||
return this.accessor.getAnySilentContainer();
|
||||
@@ -311,6 +324,95 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
|
||||
return player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable OfflinePlayer matchPlayer(@NotNull String name) {
|
||||
|
||||
// Warn if called on the main thread - if we resort to searching offline players, this may take several seconds.
|
||||
if (Bukkit.getServer().isPrimaryThread()) {
|
||||
this.getLogger().warning("Call to OpenInv#matchPlayer made on the main thread!");
|
||||
this.getLogger().warning("This can cause the server to hang, potentially severely.");
|
||||
this.getLogger().log(Level.WARNING, "Current stack trace", new Throwable("Current stack trace"));
|
||||
}
|
||||
|
||||
OfflinePlayer player;
|
||||
|
||||
try {
|
||||
UUID uuid = UUID.fromString(name);
|
||||
player = Bukkit.getOfflinePlayer(uuid);
|
||||
// Ensure player is an existing player.
|
||||
if (player.hasPlayedBefore() || player.isOnline()) {
|
||||
return player;
|
||||
}
|
||||
// Return null otherwise.
|
||||
return null;
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
// Not a UUID
|
||||
}
|
||||
|
||||
// Exact online match first.
|
||||
player = Bukkit.getServer().getPlayerExact(name);
|
||||
|
||||
if (player != null) {
|
||||
return player;
|
||||
}
|
||||
|
||||
// Cached offline match.
|
||||
PlayerProfile cachedResult = offlineLookUpCache.getIfPresent(name);
|
||||
if (cachedResult != null && cachedResult.getUniqueId() != null) {
|
||||
player = Bukkit.getOfflinePlayer(cachedResult.getUniqueId());
|
||||
// Ensure player is an existing player.
|
||||
if (player.hasPlayedBefore() || player.isOnline()) {
|
||||
return player;
|
||||
}
|
||||
// Return null otherwise.
|
||||
return null;
|
||||
}
|
||||
|
||||
// Exact offline match second - ensure offline access works when matchable users are online.
|
||||
player = Bukkit.getServer().getOfflinePlayer(name);
|
||||
|
||||
if (player.hasPlayedBefore()) {
|
||||
offlineLookUpCache.put(name, player.getPlayerProfile());
|
||||
return player;
|
||||
}
|
||||
|
||||
// Inexact online match.
|
||||
player = Bukkit.getServer().getPlayer(name);
|
||||
|
||||
if (player != null) {
|
||||
return player;
|
||||
}
|
||||
|
||||
// Finally, inexact offline match.
|
||||
float bestMatch = 0;
|
||||
for (OfflinePlayer offline : Bukkit.getServer().getOfflinePlayers()) {
|
||||
if (offline.getName() == null) {
|
||||
// Loaded by UUID only, name has never been looked up.
|
||||
continue;
|
||||
}
|
||||
|
||||
float currentMatch = StringMetric.compareJaroWinkler(name, offline.getName());
|
||||
|
||||
if (currentMatch == 1.0F) {
|
||||
return offline;
|
||||
}
|
||||
|
||||
if (currentMatch > bestMatch) {
|
||||
bestMatch = currentMatch;
|
||||
player = offline;
|
||||
}
|
||||
}
|
||||
|
||||
if (player != null) {
|
||||
// If a match was found, store it.
|
||||
offlineLookUpCache.put(name, player.getPlayerProfile());
|
||||
return player;
|
||||
}
|
||||
|
||||
// No players have ever joined the server.
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload(@NotNull final OfflinePlayer offline) {
|
||||
setPlayerOffline(offline, OfflineHandler.REMOVE_AND_CLOSE);
|
||||
@@ -362,7 +464,7 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
|
||||
public @Nullable String getLocalizedMessage(
|
||||
@NotNull CommandSender sender,
|
||||
@NotNull String key,
|
||||
String @NotNull ... replacements) {
|
||||
Replacement @NotNull ... replacements) {
|
||||
return this.languageManager.getValue(key, getLocale(sender), replacements);
|
||||
}
|
||||
|
||||
@@ -382,7 +484,7 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
|
||||
}
|
||||
}
|
||||
|
||||
public void sendMessage(@NotNull CommandSender sender, @NotNull String key, String @NotNull... replacements) {
|
||||
public void sendMessage(@NotNull CommandSender sender, @NotNull String key, Replacement @NotNull... replacements) {
|
||||
String message = getLocalizedMessage(sender, key, replacements);
|
||||
|
||||
if (message != null && !message.isEmpty()) {
|
||||
@@ -492,6 +594,45 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
|
||||
void setPlayerOnline(@NotNull Player player) {
|
||||
setPlayerOnline(inventories, player, player::updateInventory);
|
||||
setPlayerOnline(enderChests, player, null);
|
||||
|
||||
if (player.hasPlayedBefore()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// New player may have a name that already points to someone else in lookup cache.
|
||||
String name = player.getName();
|
||||
this.offlineLookUpCache.invalidate(name);
|
||||
|
||||
// If no offline matches are mapped, don't hit scheduler.
|
||||
if (this.offlineLookUpCache.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// New player may also be a more exact match than one already in the cache.
|
||||
// I.e. new player "lava1" is a better match for "lava" than "lava123"
|
||||
// Player joins are already quite intensive, so this is run on a delay.
|
||||
this.getServer().getScheduler().runTaskLaterAsynchronously(this, () -> {
|
||||
Iterator<Map.Entry<String, PlayerProfile>> iterator = this.offlineLookUpCache.asMap().entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry<String, PlayerProfile> entry = iterator.next();
|
||||
String oldMatch = entry.getValue().getName();
|
||||
|
||||
// Shouldn't be possible - all profiles should be complete.
|
||||
if (oldMatch == null) {
|
||||
iterator.remove();
|
||||
continue;
|
||||
}
|
||||
|
||||
String lookup = entry.getKey();
|
||||
float oldMatchScore = StringMetric.compareJaroWinkler(lookup, oldMatch);
|
||||
float newMatchScore = StringMetric.compareJaroWinkler(lookup, name);
|
||||
|
||||
// If new match exceeds old match, delete old match.
|
||||
if (newMatchScore > oldMatchScore) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}, 101L); // Odd delay for pseudo load balancing; Player tasks are usually scheduled with full seconds.
|
||||
}
|
||||
|
||||
private void setPlayerOnline(
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.lishid.openinv.commands;
|
||||
|
||||
import com.lishid.openinv.OpenInv;
|
||||
import com.lishid.openinv.util.TabCompleter;
|
||||
import com.lishid.openinv.util.lang.Replacement;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
@@ -69,7 +70,11 @@ public class ContainerSettingCommand implements TabExecutor {
|
||||
onOff = String.valueOf(getSetting.test(player));
|
||||
}
|
||||
|
||||
plugin.sendMessage(sender, "messages.info.settingState","%setting%", any ? "AnyContainer" : "SilentContainer", "%state%", onOff);
|
||||
plugin.sendMessage(
|
||||
sender,
|
||||
"messages.info.settingState",
|
||||
new Replacement("%setting%", any ? "AnyContainer" : "SilentContainer"),
|
||||
new Replacement("%state%", onOff));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.lishid.openinv.OpenInv;
|
||||
import com.lishid.openinv.internal.ISpecialInventory;
|
||||
import com.lishid.openinv.util.Permissions;
|
||||
import com.lishid.openinv.util.TabCompleter;
|
||||
import com.lishid.openinv.util.lang.Replacement;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -57,19 +58,23 @@ public class OpenInvCommand implements TabExecutor {
|
||||
return true;
|
||||
}
|
||||
|
||||
// History management
|
||||
String history = (openInv ? this.openInvHistory : this.openEnderHistory).get(player);
|
||||
String noArgValue;
|
||||
if (plugin.noArgsOpensSelf()) {
|
||||
noArgValue = player.getUniqueId().toString();
|
||||
} else {
|
||||
// History management
|
||||
noArgValue = (openInv ? this.openInvHistory : this.openEnderHistory).get(player);
|
||||
|
||||
if (history == null || history.isEmpty()) {
|
||||
history = player.getName();
|
||||
(openInv ? this.openInvHistory : this.openEnderHistory).put(player, history);
|
||||
if (noArgValue == null || noArgValue.isEmpty()) {
|
||||
noArgValue = player.getUniqueId().toString();
|
||||
(openInv ? this.openInvHistory : this.openEnderHistory).put(player, noArgValue);
|
||||
}
|
||||
}
|
||||
|
||||
final String name;
|
||||
|
||||
// Read from history if target is not named
|
||||
if (args.length < 1) {
|
||||
name = history;
|
||||
name = noArgValue;
|
||||
} else {
|
||||
name = args[0];
|
||||
}
|
||||
@@ -171,22 +176,28 @@ public class OpenInvCommand implements TabExecutor {
|
||||
// Protected check
|
||||
if (!Permissions.OVERRIDE.hasPermission(player)
|
||||
&& Permissions.EXEMPT.hasPermission(onlineTarget)) {
|
||||
plugin.sendMessage(player, "messages.error.permissionExempt",
|
||||
"%target%", onlineTarget.getDisplayName());
|
||||
plugin.sendMessage(
|
||||
player,
|
||||
"messages.error.permissionExempt",
|
||||
new Replacement("%target%", onlineTarget.getDisplayName()));
|
||||
return;
|
||||
}
|
||||
|
||||
// Crossworld check
|
||||
if (!Permissions.CROSSWORLD.hasPermission(player)
|
||||
&& !onlineTarget.getWorld().equals(player.getWorld())) {
|
||||
plugin.sendMessage(player, "messages.error.permissionCrossWorld",
|
||||
"%target%", onlineTarget.getDisplayName());
|
||||
plugin.sendMessage(
|
||||
player,
|
||||
"messages.error.permissionCrossWorld",
|
||||
new Replacement("%target%", onlineTarget.getDisplayName()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Record the target
|
||||
(openinv ? this.openInvHistory : this.openEnderHistory).put(player, target.getUniqueId().toString());
|
||||
if (!plugin.noArgsOpensSelf()) {
|
||||
// Record the target
|
||||
(openinv ? this.openInvHistory : this.openEnderHistory).put(player, target.getUniqueId().toString());
|
||||
}
|
||||
|
||||
// Create the inventory
|
||||
final ISpecialInventory inv;
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.lishid.openinv.commands;
|
||||
|
||||
import com.lishid.openinv.OpenInv;
|
||||
import com.lishid.openinv.util.TabCompleter;
|
||||
import com.lishid.openinv.util.lang.Replacement;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.bukkit.Chunk;
|
||||
@@ -57,7 +58,10 @@ public class SearchContainerCommand implements TabExecutor {
|
||||
Material material = Material.getMaterial(args[0].toUpperCase());
|
||||
|
||||
if (material == null) {
|
||||
plugin.sendMessage(sender, "messages.error.invalidMaterial", "%target%", args[0]);
|
||||
plugin.sendMessage(
|
||||
sender,
|
||||
"messages.error.invalidMaterial",
|
||||
new Replacement("%target%", args[0]));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -72,6 +76,10 @@ public class SearchContainerCommand implements TabExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
// Clamp radius.
|
||||
int configMax = plugin.getConfig().getInt("settings.command.searchcontainer.max-radius", 10);
|
||||
radius = Math.max(0, Math.min(radius, configMax));
|
||||
|
||||
World world = senderPlayer.getWorld();
|
||||
Chunk centerChunk = senderPlayer.getLocation().getChunk();
|
||||
StringBuilder locations = new StringBuilder();
|
||||
@@ -100,13 +108,18 @@ public class SearchContainerCommand implements TabExecutor {
|
||||
if (locations.length() > 0) {
|
||||
locations.delete(locations.length() - 2, locations.length());
|
||||
} else {
|
||||
plugin.sendMessage(sender, "messages.info.container.noMatches",
|
||||
"%target%", material.name());
|
||||
plugin.sendMessage(
|
||||
sender,
|
||||
"messages.info.container.noMatches",
|
||||
new Replacement("%target%", material.name()));
|
||||
return true;
|
||||
}
|
||||
|
||||
plugin.sendMessage(sender, "messages.info.container.matches",
|
||||
"%target%", material.name(), "%detail%", locations.toString());
|
||||
plugin.sendMessage(
|
||||
sender,
|
||||
"messages.info.container.matches",
|
||||
new Replacement("%target%", material.name()),
|
||||
new Replacement("%detail%", locations.toString()));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.lishid.openinv.commands;
|
||||
|
||||
import com.lishid.openinv.OpenInv;
|
||||
import com.lishid.openinv.util.TabCompleter;
|
||||
import com.lishid.openinv.util.lang.Replacement;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.bukkit.Material;
|
||||
@@ -114,14 +115,18 @@ public class SearchEnchantCommand implements TabExecutor {
|
||||
// Matches found, delete trailing comma and space
|
||||
players.delete(players.length() - 2, players.length());
|
||||
} else {
|
||||
plugin.sendMessage(sender, "messages.info.player.noMatches",
|
||||
"%target%", (enchant != null ? enchant.getKey().toString() : "") + " >= " + level);
|
||||
plugin.sendMessage(
|
||||
sender,
|
||||
"messages.info.player.noMatches",
|
||||
new Replacement("%target%", (enchant != null ? enchant.getKey().toString() : "") + " >= " + level));
|
||||
return true;
|
||||
}
|
||||
|
||||
plugin.sendMessage(sender, "messages.info.player.matches",
|
||||
"%target%", (enchant != null ? enchant.getKey().toString() : "") + " >= " + level,
|
||||
"%detail%", players.toString());
|
||||
plugin.sendMessage(
|
||||
sender,
|
||||
"messages.info.player.matches",
|
||||
new Replacement("%target%", (enchant != null ? enchant.getKey().toString() : "") + " >= " + level),
|
||||
new Replacement("%detail%", players.toString()));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2021 lishid. All rights reserved.
|
||||
* Copyright (C) 2011-2022 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -18,6 +18,7 @@ package com.lishid.openinv.commands;
|
||||
|
||||
import com.lishid.openinv.OpenInv;
|
||||
import com.lishid.openinv.util.TabCompleter;
|
||||
import com.lishid.openinv.util.lang.Replacement;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.bukkit.Material;
|
||||
@@ -46,7 +47,10 @@ public class SearchInvCommand implements TabExecutor {
|
||||
}
|
||||
|
||||
if (material == null) {
|
||||
plugin.sendMessage(sender, "messages.error.invalidMaterial", "%target%", args.length > 0 ? args[0] : "null");
|
||||
plugin.sendMessage(
|
||||
sender,
|
||||
"messages.error.invalidMaterial",
|
||||
new Replacement("%target%", args.length > 0 ? args[0] : "null"));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -56,7 +60,10 @@ public class SearchInvCommand implements TabExecutor {
|
||||
try {
|
||||
count = Integer.parseInt(args[1]);
|
||||
} catch (NumberFormatException ex) {
|
||||
plugin.sendMessage(sender, "messages.error.invalidNumber", "%target%", args[1]);
|
||||
plugin.sendMessage(
|
||||
sender,
|
||||
"messages.error.invalidNumber",
|
||||
new Replacement("%target%", args[1]));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -74,13 +81,18 @@ public class SearchInvCommand implements TabExecutor {
|
||||
if (players.length() > 0) {
|
||||
players.delete(players.length() - 2, players.length());
|
||||
} else {
|
||||
plugin.sendMessage(sender, "messages.info.player.noMatches",
|
||||
"%target%", material.name());
|
||||
plugin.sendMessage(
|
||||
sender,
|
||||
"messages.info.player.noMatches",
|
||||
new Replacement("%target%", material.name()));
|
||||
return true;
|
||||
}
|
||||
|
||||
plugin.sendMessage(sender, "messages.info.player.matches",
|
||||
"%target%", material.name(), "%detail%", players.toString());
|
||||
plugin.sendMessage(
|
||||
sender,
|
||||
"messages.info.player.matches",
|
||||
new Replacement("%target%", material.name()),
|
||||
new Replacement("%detail%", players.toString()));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.lishid.openinv.internal;
|
||||
|
||||
import com.lishid.openinv.OpenInv;
|
||||
import com.lishid.openinv.util.lang.Replacement;
|
||||
import java.util.Objects;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -73,8 +74,7 @@ public class OpenInventoryView extends InventoryView {
|
||||
.getLocalizedMessage(
|
||||
player,
|
||||
titleKey,
|
||||
"%player%",
|
||||
owner.getName());
|
||||
new Replacement("%player%", owner.getName()));
|
||||
title = Objects.requireNonNullElseGet(localTitle, () -> owner.getName() + titleDefaultSuffix);
|
||||
}
|
||||
|
||||
|
||||
@@ -57,6 +57,9 @@ public record ConfigUpdater(OpenInv plugin) {
|
||||
if (version < 5) {
|
||||
updateConfig4To5();
|
||||
}
|
||||
if (version < 6) {
|
||||
updateConfig5To6();
|
||||
}
|
||||
|
||||
plugin.getServer().getScheduler().runTask(plugin, () -> {
|
||||
plugin.saveConfig();
|
||||
@@ -65,6 +68,14 @@ public record ConfigUpdater(OpenInv plugin) {
|
||||
});
|
||||
}
|
||||
|
||||
private void updateConfig5To6() {
|
||||
plugin.getServer().getScheduler().runTask(plugin, () -> {
|
||||
plugin.getConfig().set("settings.command.open.no-args-opens-self", false);
|
||||
plugin.getConfig().set("settings.command.searchcontainer.max-radius", 10);
|
||||
plugin.getConfig().set("config-version", 6);
|
||||
});
|
||||
}
|
||||
|
||||
private void updateConfig4To5() {
|
||||
plugin.getServer().getScheduler().runTask(plugin, () -> {
|
||||
plugin.getConfig().set("settings.disable-offline-access", false);
|
||||
|
||||
@@ -1,168 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2022 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.util;
|
||||
|
||||
import com.lishid.openinv.OpenInv;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A simple language manager supporting both custom and bundled languages.
|
||||
*
|
||||
* @author Jikoo
|
||||
*/
|
||||
public class LanguageManager {
|
||||
|
||||
private final OpenInv plugin;
|
||||
private final String defaultLocale;
|
||||
private final Map<String, YamlConfiguration> locales;
|
||||
|
||||
public LanguageManager(@NotNull OpenInv plugin, @NotNull String defaultLocale) {
|
||||
this.plugin = plugin;
|
||||
this.defaultLocale = defaultLocale;
|
||||
this.locales = new HashMap<>();
|
||||
getOrLoadLocale(defaultLocale);
|
||||
}
|
||||
|
||||
private @NotNull YamlConfiguration getOrLoadLocale(@NotNull String locale) {
|
||||
YamlConfiguration loaded = locales.get(locale);
|
||||
if (loaded != null) {
|
||||
return loaded;
|
||||
}
|
||||
|
||||
InputStream resourceStream = plugin.getResource("locale/" + locale + ".yml");
|
||||
YamlConfiguration localeConfigDefaults;
|
||||
if (resourceStream == null) {
|
||||
localeConfigDefaults = new YamlConfiguration();
|
||||
} else {
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceStream))) {
|
||||
localeConfigDefaults = YamlConfiguration.loadConfiguration(reader);
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().log(Level.WARNING, "[LanguageManager] Unable to load resource " + locale + ".yml", e);
|
||||
localeConfigDefaults = new YamlConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
File file = new File(plugin.getDataFolder(), locale + ".yml");
|
||||
YamlConfiguration localeConfig;
|
||||
|
||||
if (!file.exists()) {
|
||||
localeConfig = localeConfigDefaults;
|
||||
try {
|
||||
localeConfigDefaults.save(file);
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().log(Level.WARNING, "[LanguageManager] Unable to save resource " + locale + ".yml", e);
|
||||
}
|
||||
} else {
|
||||
localeConfig = YamlConfiguration.loadConfiguration(file);
|
||||
|
||||
// Add new language keys
|
||||
List<String> newKeys = new ArrayList<>();
|
||||
for (String key : localeConfigDefaults.getKeys(true)) {
|
||||
if (localeConfigDefaults.isConfigurationSection(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (localeConfig.isSet(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
localeConfig.set(key, localeConfigDefaults.get(key));
|
||||
newKeys.add(key);
|
||||
}
|
||||
|
||||
if (!newKeys.isEmpty()) {
|
||||
plugin.getLogger().info("[LanguageManager] Added new language keys: " + String.join(", ", newKeys));
|
||||
try {
|
||||
localeConfig.save(file);
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().log(Level.WARNING, "[LanguageManager] Unable to save resource " + locale + ".yml", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!locale.equals(defaultLocale)) {
|
||||
localeConfigDefaults = locales.get(defaultLocale);
|
||||
|
||||
// Check for missing keys
|
||||
List<String> newKeys = new ArrayList<>();
|
||||
for (String key : localeConfigDefaults.getKeys(true)) {
|
||||
if (localeConfigDefaults.isConfigurationSection(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (localeConfig.isSet(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
newKeys.add(key);
|
||||
}
|
||||
|
||||
if (!newKeys.isEmpty()) {
|
||||
plugin.getLogger().info("[LanguageManager] Missing translations from " + locale + ".yml: " + String.join(", ", newKeys));
|
||||
}
|
||||
|
||||
// Fall through to default locale
|
||||
localeConfig.setDefaults(localeConfigDefaults);
|
||||
}
|
||||
|
||||
locales.put(locale, localeConfig);
|
||||
return localeConfig;
|
||||
}
|
||||
|
||||
public @Nullable String getValue(@NotNull String key, @Nullable String locale) {
|
||||
String value = getOrLoadLocale(locale == null ? defaultLocale : locale.toLowerCase()).getString(key);
|
||||
if (value == null || value.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
value = ChatColor.translateAlternateColorCodes('&', value);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public @Nullable String getValue(@NotNull String key, @Nullable String locale, String @NotNull ... replacements) {
|
||||
if (replacements.length % 2 != 0) {
|
||||
plugin.getLogger().log(Level.WARNING, "[LanguageManager] Replacement data is uneven", new Exception());
|
||||
}
|
||||
|
||||
String value = getValue(key, locale);
|
||||
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int i = 0; i < replacements.length; i += 2) {
|
||||
value = value.replace(replacements[i], replacements[i + 1]);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2022 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.util.lang;
|
||||
|
||||
import com.lishid.openinv.OpenInv;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.logging.Level;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.configuration.Configuration;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A simple language manager supporting both custom and bundled languages.
|
||||
*
|
||||
* @author Jikoo
|
||||
*/
|
||||
public class LanguageManager {
|
||||
|
||||
private final OpenInv plugin;
|
||||
private final String defaultLocale;
|
||||
private final Map<String, YamlConfiguration> locales;
|
||||
|
||||
public LanguageManager(@NotNull OpenInv plugin, @NotNull String defaultLocale) {
|
||||
this.plugin = plugin;
|
||||
this.defaultLocale = defaultLocale;
|
||||
this.locales = new HashMap<>();
|
||||
getOrLoadLocale(defaultLocale);
|
||||
}
|
||||
|
||||
private @NotNull YamlConfiguration getOrLoadLocale(@NotNull String locale) {
|
||||
YamlConfiguration loaded = locales.get(locale);
|
||||
if (loaded != null) {
|
||||
return loaded;
|
||||
}
|
||||
|
||||
File file = new File(plugin.getDataFolder(), locale + ".yml");
|
||||
|
||||
// Load locale config from disk and bundled locale defaults.
|
||||
YamlConfiguration localeConfig = loadLocale(locale, file);
|
||||
|
||||
// If the locale is not the default locale, also handle any missing translations from the default locale.
|
||||
if (!locale.equals(defaultLocale)) {
|
||||
addTranslationFallthrough(locale, localeConfig, file);
|
||||
|
||||
if (plugin.getConfig().getBoolean("settings.secret.warn-about-guess-section", true)
|
||||
&& localeConfig.isConfigurationSection("guess")) {
|
||||
// Warn that guess section exists. This should run once per language per server restart
|
||||
// when accessed by a user to hint to server owners that they can make UX improvements.
|
||||
plugin.getLogger().info(() -> "[LanguageManager] Missing translations from " + locale + ".yml! Check the guess section!");
|
||||
}
|
||||
}
|
||||
|
||||
locales.put(locale, localeConfig);
|
||||
return localeConfig;
|
||||
}
|
||||
|
||||
private @NotNull YamlConfiguration loadLocale(
|
||||
@NotNull String locale,
|
||||
@NotNull File file) {
|
||||
// Load defaults from the plugin's bundled resources.
|
||||
InputStream resourceStream = plugin.getResource("locale/" + locale + ".yml");
|
||||
YamlConfiguration localeConfigDefaults;
|
||||
if (resourceStream == null) {
|
||||
localeConfigDefaults = new YamlConfiguration();
|
||||
} else {
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceStream))) {
|
||||
localeConfigDefaults = YamlConfiguration.loadConfiguration(reader);
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to load resource " + locale + ".yml");
|
||||
localeConfigDefaults = new YamlConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
if (!file.exists()) {
|
||||
// If the file does not exist on disk, save bundled defaults.
|
||||
try {
|
||||
localeConfigDefaults.save(file);
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to save resource " + locale + ".yml");
|
||||
}
|
||||
// Return loaded bundled locale.
|
||||
return localeConfigDefaults;
|
||||
}
|
||||
|
||||
// If the file does exist on disk, load it.
|
||||
YamlConfiguration localeConfig = YamlConfiguration.loadConfiguration(file);
|
||||
// Check for missing translations from the bundled file.
|
||||
List<String> newKeys = getMissingKeys(localeConfigDefaults, localeConfig::isSet);
|
||||
|
||||
if (newKeys.isEmpty()) {
|
||||
return localeConfig;
|
||||
}
|
||||
|
||||
// Get guess section for missing keys.
|
||||
ConfigurationSection guess = localeConfig.getConfigurationSection("guess");
|
||||
|
||||
for (String newKey : newKeys) {
|
||||
// Set all missing keys to defaults.
|
||||
localeConfig.set(newKey, localeConfigDefaults.get(newKey));
|
||||
|
||||
// Delete relevant guess keys in case this is a new translation.
|
||||
if (guess != null) {
|
||||
guess.set(newKey, null);
|
||||
}
|
||||
}
|
||||
|
||||
// If guess section is empty, delete it.
|
||||
if (guess != null && guess.getKeys(false).isEmpty()) {
|
||||
localeConfig.set("guess", null);
|
||||
}
|
||||
|
||||
plugin.getLogger().info(() -> "[LanguageManager] Added new translation keys to " + locale + ".yml: " + String.join(", ", newKeys));
|
||||
|
||||
// Write new keys to disk.
|
||||
try {
|
||||
localeConfig.save(file);
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to save resource " + locale + ".yml");
|
||||
}
|
||||
|
||||
return localeConfig;
|
||||
}
|
||||
|
||||
private void addTranslationFallthrough(
|
||||
@NotNull String locale,
|
||||
@NotNull YamlConfiguration localeConfig,
|
||||
@NotNull File file) {
|
||||
YamlConfiguration defaultLocaleConfig = locales.get(defaultLocale);
|
||||
|
||||
// Get missing keys. Keys that already have a guess value are not new and don't need to trigger another write.
|
||||
List<String> missingKeys = getMissingKeys(
|
||||
defaultLocaleConfig,
|
||||
key -> localeConfig.isSet(key) || localeConfig.isSet("guess." + key));
|
||||
|
||||
if (!missingKeys.isEmpty()) {
|
||||
// Set up guess section for missing keys.
|
||||
for (String key : missingKeys) {
|
||||
localeConfig.set("guess." + key, defaultLocaleConfig.get(key));
|
||||
}
|
||||
|
||||
// Write modified guess section to disk.
|
||||
try {
|
||||
localeConfig.save(file);
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to save resource " + locale + ".yml");
|
||||
}
|
||||
}
|
||||
|
||||
// Fall through to default locale.
|
||||
localeConfig.setDefaults(defaultLocaleConfig);
|
||||
}
|
||||
|
||||
private @NotNull List<String> getMissingKeys(
|
||||
@NotNull Configuration configurationDefault,
|
||||
@NotNull Predicate<String> nodeSetPredicate) {
|
||||
List<String> missingKeys = new ArrayList<>();
|
||||
for (String key : configurationDefault.getKeys(true)) {
|
||||
if (!configurationDefault.isConfigurationSection(key) && !nodeSetPredicate.test(key)) {
|
||||
// Missing keys are non-section keys that fail the predicate.
|
||||
missingKeys.add(key);
|
||||
}
|
||||
}
|
||||
return missingKeys;
|
||||
}
|
||||
|
||||
public @Nullable String getValue(@NotNull String key, @Nullable String locale) {
|
||||
String value = getOrLoadLocale(locale == null ? defaultLocale : locale.toLowerCase()).getString(key);
|
||||
if (value == null || value.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
value = ChatColor.translateAlternateColorCodes('&', value);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public @Nullable String getValue(@NotNull String key, @Nullable String locale, Replacement @NotNull ... replacements) {
|
||||
String value = getValue(key, locale);
|
||||
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (Replacement replacement : replacements) {
|
||||
value = value.replace(replacement.placeholder(), replacement.value());
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2022 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.util.lang;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A data holder for string replacement in translations.
|
||||
*
|
||||
* @param placeholder the placeholder to be replaced
|
||||
* @param value the value to insert
|
||||
*/
|
||||
public record Replacement(@NotNull String placeholder, @NotNull String value) {
|
||||
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
config-version: 5
|
||||
config-version: 6
|
||||
settings:
|
||||
command:
|
||||
open:
|
||||
no-args-opens-self: false
|
||||
searchcontainer:
|
||||
max-radius: 10
|
||||
disable-offline-access: false
|
||||
disable-saving: false
|
||||
locale: 'en_us'
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--
|
||||
~ Copyright (C) 2011-2022 lishid. All rights reserved.
|
||||
~ Copyright (C) 2011-2023 lishid. All rights reserved.
|
||||
~
|
||||
~ This program is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
@@ -21,7 +21,7 @@
|
||||
<artifactId>openinvparent</artifactId>
|
||||
<name>OpenInv</name>
|
||||
<url>http://dev.bukkit.org/bukkit-plugins/openinv/</url>
|
||||
<version>4.2.2</version>
|
||||
<version>4.3.0</version>
|
||||
|
||||
<packaging>pom</packaging>
|
||||
|
||||
@@ -40,8 +40,8 @@
|
||||
<module>api</module>
|
||||
<module>plugin</module>
|
||||
<module>internal/v1_18_R2</module>
|
||||
<module>internal/v1_19_R1</module>
|
||||
<module>internal/v1_19_R2</module>
|
||||
<module>internal/v1_19_R3</module>
|
||||
<module>assembly</module>
|
||||
</modules>
|
||||
</profile>
|
||||
@@ -72,25 +72,25 @@
|
||||
<artifactId>annotations</artifactId>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
<scope>provided</scope>
|
||||
<version>23.0.0</version>
|
||||
<version>24.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<scope>provided</scope>
|
||||
<version>1.17.1-R0.1-SNAPSHOT</version>
|
||||
<version>1.18.2-R0.1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>openinvapi</artifactId>
|
||||
<groupId>com.lishid</groupId>
|
||||
<scope>compile</scope>
|
||||
<version>4.2.2</version>
|
||||
<version>4.3.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>openinvplugincore</artifactId>
|
||||
<groupId>com.lishid</groupId>
|
||||
<scope>compile</scope>
|
||||
<version>4.2.2</version>
|
||||
<version>4.3.0</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.lishid</groupId>
|
||||
@@ -111,7 +111,7 @@
|
||||
-->
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<version>3.4.0</version>
|
||||
<version>3.5.0</version>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
@@ -150,13 +150,13 @@
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<version>3.10.1</version>
|
||||
<version>3.11.0</version>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<version>3.4.2</version>
|
||||
<version>3.5.0</version>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright (C) 2011-2021 lishid. All rights reserved.
|
||||
# Copyright (C) 2011-2023 lishid. All rights reserved.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -36,7 +36,7 @@ function get_minecraft_versions() {
|
||||
|
||||
for version in "${versions[@]}"; do
|
||||
# Append comma if variable is set, then append version
|
||||
minecraft_versions="${minecraft_versions:+${minecraft_versions},}${version%%-R*}"
|
||||
minecraft_versions="${minecraft_versions:+${minecraft_versions}, }${version%%-R*}"
|
||||
done
|
||||
|
||||
echo "${minecraft_versions}"
|
||||
|
||||
Reference in new issue
Block a user