Mirror von
https://github.com/GeyserMC/Geyser.git
synchronisiert 2024-12-26 16:12:46 +01:00
Merge remote-tracking branch 'remotes/upstream/master' into server-inventory-upstream
# Conflicts: # connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java
Dieser Commit ist enthalten in:
Commit
e87ae8f0dd
5
Jenkinsfile
vendored
5
Jenkinsfile
vendored
@ -92,8 +92,9 @@ pipeline {
|
||||
success {
|
||||
script {
|
||||
if (env.BRANCH_NAME == 'master') {
|
||||
build propagate: false, wait: false, job: 'GeyserMC/Geyser-Fabric/java-1.16'
|
||||
build propagate: false, wait: false, job: 'GeyserMC/GeyserAndroid/master'
|
||||
build propagate: false, wait: false, job: 'GeyserMC/Geyser-Fabric/java-1.16', parameters: [booleanParam(name: 'SKIP_DISCORD', value: true)]
|
||||
build propagate: false, wait: false, job: 'GeyserMC/GeyserAndroid/master', parameters: [booleanParam(name: 'SKIP_DISCORD', value: true)]
|
||||
build propagate: false, wait: false, job: 'GeyserMC/GeyserConnect/master', parameters: [booleanParam(name: 'SKIP_DISCORD', value: true)]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,6 +47,7 @@ Take a look [here](https://github.com/GeyserMC/Geyser/wiki#Setup) for how to set
|
||||
- Horse Inventory
|
||||
- Loom
|
||||
- Smithing Table
|
||||
- Grindstone
|
||||
|
||||
## What can't be fixed
|
||||
The following things can't be fixed because of Bedrock limitations. They might be fixable in the future, but not as of now.
|
||||
|
@ -52,6 +52,7 @@ public abstract class CommandManager {
|
||||
registerCommand(new VersionCommand(connector, "version", "geyser.commands.version.desc", "geyser.command.version"));
|
||||
registerCommand(new SettingsCommand(connector, "settings", "geyser.commands.settings.desc", "geyser.command.settings"));
|
||||
registerCommand(new StatisticsCommand(connector, "statistics", "geyser.commands.statistics.desc", "geyser.command.statistics"));
|
||||
registerCommand(new AdvancementsCommand(connector, "advancements", "geyser.commands.advancements.desc", "geyser.command.advancements"));
|
||||
}
|
||||
|
||||
public void registerCommand(GeyserCommand command) {
|
||||
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.command.defaults;
|
||||
|
||||
import org.geysermc.common.window.SimpleFormWindow;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.command.CommandSender;
|
||||
import org.geysermc.connector.command.GeyserCommand;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.session.cache.AdvancementsCache;
|
||||
|
||||
public class AdvancementsCommand extends GeyserCommand {
|
||||
|
||||
private final GeyserConnector connector;
|
||||
|
||||
public AdvancementsCommand(GeyserConnector connector, String name, String description, String permission) {
|
||||
super(name, description, permission);
|
||||
|
||||
this.connector = connector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, String[] args) {
|
||||
if (sender.isConsole()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure the sender is a Bedrock edition client
|
||||
GeyserSession session = null;
|
||||
if (sender instanceof GeyserSession) {
|
||||
session = (GeyserSession) sender;
|
||||
} else {
|
||||
// Needed for Spigot - sender is not an instance of GeyserSession
|
||||
for (GeyserSession otherSession : connector.getPlayers()) {
|
||||
if (sender.getName().equals(otherSession.getPlayerEntity().getUsername())) {
|
||||
session = otherSession;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (session == null) return;
|
||||
|
||||
SimpleFormWindow window = session.getAdvancementsCache().buildMenuForm();
|
||||
session.sendForm(window, AdvancementsCache.ADVANCEMENTS_MENU_FORM_ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExecutableOnConsole() {
|
||||
return false;
|
||||
}
|
||||
}
|
@ -40,7 +40,9 @@ public class BedrockProtocol {
|
||||
* Default Bedrock codec that should act as a fallback. Should represent the latest available
|
||||
* release of the game that Geyser supports.
|
||||
*/
|
||||
public static final BedrockPacketCodec DEFAULT_BEDROCK_CODEC = Bedrock_v422.V422_CODEC;
|
||||
public static final BedrockPacketCodec DEFAULT_BEDROCK_CODEC = Bedrock_v422.V422_CODEC.toBuilder()
|
||||
.minecraftVersion("1.16.201")
|
||||
.build();
|
||||
/**
|
||||
* A list of all supported Bedrock versions that can join Geyser
|
||||
*/
|
||||
@ -50,7 +52,9 @@ public class BedrockProtocol {
|
||||
SUPPORTED_BEDROCK_CODECS.add(Bedrock_v419.V419_CODEC.toBuilder()
|
||||
.minecraftVersion("1.16.100/1.16.101") // We change this as 1.16.100.60 is a beta
|
||||
.build());
|
||||
SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC);
|
||||
SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC.toBuilder()
|
||||
.minecraftVersion("1.16.200/1.16.201")
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -33,14 +33,9 @@ import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.common.AuthType;
|
||||
import org.geysermc.connector.configuration.GeyserConfiguration;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.session.cache.AdvancementsCache;
|
||||
import org.geysermc.connector.network.translators.PacketTranslatorRegistry;
|
||||
import org.geysermc.connector.utils.LanguageUtils;
|
||||
import org.geysermc.connector.utils.LoginEncryptionUtils;
|
||||
import org.geysermc.connector.utils.MathUtils;
|
||||
import org.geysermc.connector.utils.ResourcePack;
|
||||
import org.geysermc.connector.utils.ResourcePackManifest;
|
||||
import org.geysermc.connector.utils.SettingsUtils;
|
||||
import org.geysermc.connector.utils.StatisticsUtils;
|
||||
import org.geysermc.connector.utils.*;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
@ -144,12 +139,19 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||
|
||||
@Override
|
||||
public boolean handle(ModalFormResponsePacket packet) {
|
||||
if (packet.getFormId() == SettingsUtils.SETTINGS_FORM_ID) {
|
||||
switch (packet.getFormId()) {
|
||||
case AdvancementsCache.ADVANCEMENT_INFO_FORM_ID:
|
||||
return session.getAdvancementsCache().handleInfoForm(packet.getFormData());
|
||||
case AdvancementsCache.ADVANCEMENTS_LIST_FORM_ID:
|
||||
return session.getAdvancementsCache().handleListForm(packet.getFormData());
|
||||
case AdvancementsCache.ADVANCEMENTS_MENU_FORM_ID:
|
||||
return session.getAdvancementsCache().handleMenuForm(packet.getFormData());
|
||||
case SettingsUtils.SETTINGS_FORM_ID:
|
||||
return SettingsUtils.handleSettingsForm(session, packet.getFormData());
|
||||
} else if (packet.getFormId() == StatisticsUtils.STATISTICS_MENU_FORM_ID) {
|
||||
return StatisticsUtils.handleMenuForm(session, packet.getFormData());
|
||||
} else if (packet.getFormId() == StatisticsUtils.STATISTICS_LIST_FORM_ID) {
|
||||
case StatisticsUtils.STATISTICS_LIST_FORM_ID:
|
||||
return StatisticsUtils.handleListForm(session, packet.getFormData());
|
||||
case StatisticsUtils.STATISTICS_MENU_FORM_ID:
|
||||
return StatisticsUtils.handleMenuForm(session, packet.getFormData());
|
||||
}
|
||||
|
||||
return LoginEncryptionUtils.authenticateFromForm(session, connector, packet.getFormId(), packet.getFormData());
|
||||
|
@ -124,6 +124,7 @@ public class GeyserSession implements CommandSender {
|
||||
|
||||
private final SessionPlayerEntity playerEntity;
|
||||
|
||||
private AdvancementsCache advancementsCache;
|
||||
private BookEditCache bookEditCache;
|
||||
private ChunkCache chunkCache;
|
||||
private EntityCache entityCache;
|
||||
@ -384,6 +385,7 @@ public class GeyserSession implements CommandSender {
|
||||
this.connector = connector;
|
||||
this.upstream = new UpstreamSession(bedrockServerSession);
|
||||
|
||||
this.advancementsCache = new AdvancementsCache(this);
|
||||
this.bookEditCache = new BookEditCache(this);
|
||||
this.chunkCache = new ChunkCache(this);
|
||||
this.entityCache = new EntityCache(this);
|
||||
@ -409,6 +411,10 @@ public class GeyserSession implements CommandSender {
|
||||
|
||||
connector.getPlayers().forEach(player -> this.emotes.addAll(player.getEmotes()));
|
||||
|
||||
// Make a copy to prevent ConcurrentModificationException
|
||||
final List<GeyserSession> tmpPlayers = new ArrayList<>(connector.getPlayers());
|
||||
tmpPlayers.forEach(player -> this.emotes.addAll(player.getEmotes()));
|
||||
|
||||
bedrockServerSession.addDisconnectHandler(disconnectReason -> {
|
||||
connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.disconnect", bedrockServerSession.getAddress().getAddress(), disconnectReason));
|
||||
|
||||
@ -723,6 +729,7 @@ public class GeyserSession implements CommandSender {
|
||||
tickThread.cancel(true);
|
||||
}
|
||||
|
||||
this.advancementsCache = null;
|
||||
this.bookEditCache = null;
|
||||
this.chunkCache = null;
|
||||
this.entityCache = null;
|
||||
@ -862,6 +869,7 @@ public class GeyserSession implements CommandSender {
|
||||
startGamePacket.setLevelName(serverName);
|
||||
|
||||
startGamePacket.setPremiumWorldTemplateId("00000000-0000-0000-0000-000000000000");
|
||||
// startGamePacket.setCurrentTick(0);
|
||||
startGamePacket.setEnchantmentSeed(0);
|
||||
startGamePacket.setMultiplayerCorrelationId("");
|
||||
startGamePacket.setItemEntries(ItemRegistry.ITEMS);
|
||||
|
321
connector/src/main/java/org/geysermc/connector/network/session/cache/AdvancementsCache.java
vendored
Normale Datei
321
connector/src/main/java/org/geysermc/connector/network/session/cache/AdvancementsCache.java
vendored
Normale Datei
@ -0,0 +1,321 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.network.session.cache;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.advancement.Advancement;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientAdvancementTabPacket;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.geysermc.common.window.SimpleFormWindow;
|
||||
import org.geysermc.common.window.button.FormButton;
|
||||
import org.geysermc.common.window.response.SimpleFormResponse;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||
import org.geysermc.connector.utils.GeyserAdvancement;
|
||||
import org.geysermc.connector.utils.LanguageUtils;
|
||||
import org.geysermc.connector.utils.LocaleUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class AdvancementsCache {
|
||||
|
||||
// Different form IDs
|
||||
public static final int ADVANCEMENTS_MENU_FORM_ID = 1341;
|
||||
public static final int ADVANCEMENTS_LIST_FORM_ID = 1342;
|
||||
public static final int ADVANCEMENT_INFO_FORM_ID = 1343;
|
||||
|
||||
/**
|
||||
* Stores the player's advancement progress
|
||||
*/
|
||||
@Getter
|
||||
private final Map<String, Map<String, Long>> storedAdvancementProgress = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Stores advancements for the player.
|
||||
*/
|
||||
@Getter
|
||||
private final Map<String, GeyserAdvancement> storedAdvancements = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Stores player's chosen advancement's ID and title for use in form creators.
|
||||
*/
|
||||
@Setter
|
||||
private String currentAdvancementCategoryId = null;
|
||||
|
||||
private final GeyserSession session;
|
||||
|
||||
public AdvancementsCache(GeyserSession session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a form with all advancement categories
|
||||
*
|
||||
* @return The built advancement category menu
|
||||
*/
|
||||
public SimpleFormWindow buildMenuForm() {
|
||||
// Cache the language for cleaner access
|
||||
String language = session.getClientData().getLanguageCode();
|
||||
|
||||
// Created menu window for advancement categories
|
||||
SimpleFormWindow window = new SimpleFormWindow(LocaleUtils.getLocaleString("gui.advancements", language), "");
|
||||
for (Map.Entry<String, GeyserAdvancement> advancement : storedAdvancements.entrySet()) {
|
||||
if (advancement.getValue().getParentId() == null) { // No parent means this is a root advancement
|
||||
window.getButtons().add(new FormButton(MessageTranslator.convertMessage(advancement.getValue().getDisplayData().getTitle(), language)));
|
||||
}
|
||||
}
|
||||
|
||||
if (window.getButtons().isEmpty()) {
|
||||
window.setContent(LocaleUtils.getLocaleString("advancements.empty", language));
|
||||
}
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the list of advancements
|
||||
*
|
||||
* @return The built list form
|
||||
*/
|
||||
public SimpleFormWindow buildListForm() {
|
||||
// Cache the language for easier access
|
||||
String language = session.getLocale();
|
||||
String id = currentAdvancementCategoryId;
|
||||
GeyserAdvancement categoryAdvancement = storedAdvancements.get(currentAdvancementCategoryId);
|
||||
|
||||
// Create the window
|
||||
SimpleFormWindow window = new SimpleFormWindow(MessageTranslator.convertMessage(categoryAdvancement.getDisplayData().getTitle(), language),
|
||||
MessageTranslator.convertMessage(categoryAdvancement.getDisplayData().getDescription(), language));
|
||||
|
||||
if (id != null) {
|
||||
for (Map.Entry<String, GeyserAdvancement> advancementEntry : storedAdvancements.entrySet()) {
|
||||
GeyserAdvancement advancement = advancementEntry.getValue();
|
||||
if (advancement != null) {
|
||||
if (advancement.getParentId() != null && currentAdvancementCategoryId.equals(advancement.getRootId(this))) {
|
||||
boolean earned = isEarned(advancement);
|
||||
|
||||
if (earned || !advancement.getDisplayData().isShowToast()) {
|
||||
window.getButtons().add(new FormButton("§6" + MessageTranslator.convertMessage(advancementEntry.getValue().getDisplayData().getTitle()) + "\n"));
|
||||
} else {
|
||||
window.getButtons().add(new FormButton(MessageTranslator.convertMessage(advancementEntry.getValue().getDisplayData().getTitle()) + "\n"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.getButtons().add(new FormButton(LanguageUtils.getPlayerLocaleString("gui.back", language)));
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the advancement display info based on the chosen category
|
||||
*
|
||||
* @param advancement The advancement used to create the info display
|
||||
* @return The information for the chosen advancement
|
||||
*/
|
||||
public SimpleFormWindow buildInfoForm(GeyserAdvancement advancement) {
|
||||
// Cache language for easier access
|
||||
String language = session.getLocale();
|
||||
|
||||
String earned = isEarned(advancement) ? "yes" : "no";
|
||||
|
||||
String description = getColorFromAdvancementFrameType(advancement) + MessageTranslator.convertMessage(advancement.getDisplayData().getDescription(), language);
|
||||
String earnedString = LanguageUtils.getPlayerLocaleString("geyser.advancements.earned", language, LocaleUtils.getLocaleString("gui." + earned, language));
|
||||
|
||||
/*
|
||||
Layout will look like:
|
||||
|
||||
(Form title) Stone Age
|
||||
|
||||
(Description) Mine stone with your new pickaxe
|
||||
|
||||
Earned: Yes
|
||||
Parent Advancement: Minecraft // If relevant
|
||||
*/
|
||||
|
||||
String content = description + "\n\n§f" +
|
||||
earnedString + "\n";
|
||||
if (!currentAdvancementCategoryId.equals(advancement.getParentId())) {
|
||||
// Only display the parent if it is not the category
|
||||
content += LanguageUtils.getPlayerLocaleString("geyser.advancements.parentid", language, MessageTranslator.convertMessage(storedAdvancements.get(advancement.getParentId()).getDisplayData().getTitle(), language));
|
||||
}
|
||||
SimpleFormWindow window = new SimpleFormWindow(MessageTranslator.convertMessage(advancement.getDisplayData().getTitle()), content);
|
||||
window.getButtons().add(new FormButton(LanguageUtils.getPlayerLocaleString("gui.back", language)));
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this advancement has been earned.
|
||||
*
|
||||
* @param advancement the advancement to determine
|
||||
* @return true if the advancement has been earned.
|
||||
*/
|
||||
public boolean isEarned(GeyserAdvancement advancement) {
|
||||
boolean earned = false;
|
||||
if (advancement.getRequirements().size() == 0) {
|
||||
// Minecraft handles this case, so we better as well
|
||||
return false;
|
||||
}
|
||||
Map<String, Long> progress = storedAdvancementProgress.get(advancement.getId());
|
||||
if (progress != null) {
|
||||
// Each advancement's requirement must be fulfilled
|
||||
// For example, [[zombie, blaze, skeleton]] means that one of those three categories must be achieved
|
||||
// But [[zombie], [blaze], [skeleton]] means that all three requirements must be completed
|
||||
for (List<String> requirements : advancement.getRequirements()) {
|
||||
boolean requirementsDone = false;
|
||||
for (String requirement : requirements) {
|
||||
Long obtained = progress.get(requirement);
|
||||
// -1 means that this particular component required for completing the advancement
|
||||
// has yet to be fulfilled
|
||||
if (obtained != null && !obtained.equals(-1L)) {
|
||||
requirementsDone = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!requirementsDone) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
earned = true;
|
||||
}
|
||||
return earned;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the menu form response
|
||||
*
|
||||
* @param response The response string to parse
|
||||
* @return True if the form was parsed correctly, false if not
|
||||
*/
|
||||
public boolean handleMenuForm(String response) {
|
||||
SimpleFormWindow menuForm = (SimpleFormWindow) session.getWindowCache().getWindows().get(ADVANCEMENTS_MENU_FORM_ID);
|
||||
menuForm.setResponse(response);
|
||||
|
||||
SimpleFormResponse formResponse = (SimpleFormResponse) menuForm.getResponse();
|
||||
|
||||
String id = "";
|
||||
if (formResponse != null && formResponse.getClickedButton() != null) {
|
||||
int advancementIndex = 0;
|
||||
for (Map.Entry<String, GeyserAdvancement> advancement : storedAdvancements.entrySet()) {
|
||||
if (advancement.getValue().getParentId() == null) { // Root advancement
|
||||
if (advancementIndex == formResponse.getClickedButtonId()) {
|
||||
id = advancement.getKey();
|
||||
break;
|
||||
} else {
|
||||
advancementIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!id.equals("")) {
|
||||
if (id.equals(currentAdvancementCategoryId)) {
|
||||
// The server thinks we are already on this tab
|
||||
session.sendForm(buildListForm(), ADVANCEMENTS_LIST_FORM_ID);
|
||||
} else {
|
||||
// Send a packet indicating that we intend to open this particular advancement window
|
||||
ClientAdvancementTabPacket packet = new ClientAdvancementTabPacket(id);
|
||||
session.sendDownstreamPacket(packet);
|
||||
// Wait for a response there
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the list form response (Advancement category choice)
|
||||
*
|
||||
* @param response The response string to parse
|
||||
* @return True if the form was parsed correctly, false if not
|
||||
*/
|
||||
public boolean handleListForm(String response) {
|
||||
SimpleFormWindow listForm = (SimpleFormWindow) session.getWindowCache().getWindows().get(ADVANCEMENTS_LIST_FORM_ID);
|
||||
listForm.setResponse(response);
|
||||
|
||||
SimpleFormResponse formResponse = (SimpleFormResponse) listForm.getResponse();
|
||||
|
||||
if (!listForm.isClosed() && formResponse != null && formResponse.getClickedButton() != null) {
|
||||
GeyserAdvancement advancement = null;
|
||||
int advancementIndex = 0;
|
||||
// Loop around to find the advancement that the client pressed
|
||||
for (GeyserAdvancement advancementEntry : storedAdvancements.values()) {
|
||||
if (advancementEntry.getParentId() != null &&
|
||||
currentAdvancementCategoryId.equals(advancementEntry.getRootId(this))) {
|
||||
if (advancementIndex == formResponse.getClickedButtonId()) {
|
||||
advancement = advancementEntry;
|
||||
break;
|
||||
} else {
|
||||
advancementIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (advancement != null) {
|
||||
session.sendForm(buildInfoForm(advancement), ADVANCEMENT_INFO_FORM_ID);
|
||||
} else {
|
||||
session.sendForm(buildMenuForm(), ADVANCEMENTS_MENU_FORM_ID);
|
||||
// Indicate that we have closed the current advancement tab
|
||||
session.sendDownstreamPacket(new ClientAdvancementTabPacket());
|
||||
}
|
||||
} else {
|
||||
// Indicate that we have closed the current advancement tab
|
||||
session.sendDownstreamPacket(new ClientAdvancementTabPacket());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the info form response
|
||||
*
|
||||
* @param response The response string to parse
|
||||
* @return True if the form was parsed correctly, false if not
|
||||
*/
|
||||
public boolean handleInfoForm(String response) {
|
||||
SimpleFormWindow listForm = (SimpleFormWindow) session.getWindowCache().getWindows().get(ADVANCEMENT_INFO_FORM_ID);
|
||||
listForm.setResponse(response);
|
||||
|
||||
SimpleFormResponse formResponse = (SimpleFormResponse) listForm.getResponse();
|
||||
|
||||
if (!listForm.isClosed() && formResponse != null && formResponse.getClickedButton() != null) {
|
||||
session.sendForm(buildListForm(), ADVANCEMENTS_LIST_FORM_ID);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getColorFromAdvancementFrameType(GeyserAdvancement advancement) {
|
||||
String base = "\u00a7";
|
||||
if (advancement.getDisplayData().getFrameType() == Advancement.DisplayData.FrameType.CHALLENGE) {
|
||||
return base + "5";
|
||||
}
|
||||
return base + "a"; // Used for types TASK and GOAL
|
||||
}
|
||||
}
|
@ -37,10 +37,10 @@ import org.geysermc.connector.network.session.GeyserSession;
|
||||
|
||||
public class WindowCache {
|
||||
|
||||
private GeyserSession session;
|
||||
private final GeyserSession session;
|
||||
|
||||
@Getter
|
||||
private Int2ObjectMap<FormWindow> windows = new Int2ObjectOpenHashMap<>();
|
||||
private final Int2ObjectMap<FormWindow> windows = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
public WindowCache(GeyserSession session) {
|
||||
this.session = session;
|
||||
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.network.translators.java;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerAdvancementTabPacket;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.session.cache.AdvancementsCache;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
|
||||
/**
|
||||
* Indicates that the client should open a particular advancement tab
|
||||
*/
|
||||
@Translator(packet = ServerAdvancementTabPacket.class)
|
||||
public class JavaAdvancementsTabTranslator extends PacketTranslator<ServerAdvancementTabPacket> {
|
||||
|
||||
@Override
|
||||
public void translate(ServerAdvancementTabPacket packet, GeyserSession session) {
|
||||
session.getAdvancementsCache().setCurrentAdvancementCategoryId(packet.getTabId());
|
||||
session.sendForm(session.getAdvancementsCache().buildListForm(), AdvancementsCache.ADVANCEMENTS_LIST_FORM_ID);
|
||||
}
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.network.translators.java;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.advancement.Advancement;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerAdvancementsPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.SetTitlePacket;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||
import org.geysermc.connector.network.session.cache.AdvancementsCache;
|
||||
import org.geysermc.connector.utils.GeyserAdvancement;
|
||||
import org.geysermc.connector.utils.LocaleUtils;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Translator(packet = ServerAdvancementsPacket.class)
|
||||
public class JavaAdvancementsTranslator extends PacketTranslator<ServerAdvancementsPacket> {
|
||||
|
||||
@Override
|
||||
public void translate(ServerAdvancementsPacket packet, GeyserSession session) {
|
||||
AdvancementsCache advancementsCache = session.getAdvancementsCache();
|
||||
if (packet.isReset()) {
|
||||
advancementsCache.getStoredAdvancements().clear();
|
||||
advancementsCache.getStoredAdvancementProgress().clear();
|
||||
}
|
||||
|
||||
// Removes removed advancements from player's stored advancements
|
||||
for (String removedAdvancement : packet.getRemovedAdvancements()) {
|
||||
advancementsCache.getStoredAdvancements().remove(removedAdvancement);
|
||||
}
|
||||
|
||||
advancementsCache.getStoredAdvancementProgress().putAll(packet.getProgress());
|
||||
|
||||
sendToolbarAdvancementUpdates(session, packet);
|
||||
|
||||
// Adds advancements to the player's stored advancements when advancements are sent
|
||||
for (Advancement advancement : packet.getAdvancements()) {
|
||||
if (advancement.getDisplayData() != null && !advancement.getDisplayData().isHidden()) {
|
||||
GeyserAdvancement geyserAdvancement = GeyserAdvancement.from(advancement);
|
||||
advancementsCache.getStoredAdvancements().put(advancement.getId(), geyserAdvancement);
|
||||
} else {
|
||||
advancementsCache.getStoredAdvancements().remove(advancement.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle all advancements progress updates
|
||||
*/
|
||||
public void sendToolbarAdvancementUpdates(GeyserSession session, ServerAdvancementsPacket packet) {
|
||||
if (packet.isReset()) {
|
||||
// Advancements are being cleared, so they can't be granted
|
||||
return;
|
||||
}
|
||||
for (Map.Entry<String, Map<String, Long>> progress : packet.getProgress().entrySet()) {
|
||||
GeyserAdvancement advancement = session.getAdvancementsCache().getStoredAdvancements().get(progress.getKey());
|
||||
if (advancement != null && advancement.getDisplayData() != null) {
|
||||
if (session.getAdvancementsCache().isEarned(advancement)) {
|
||||
// Java uses some pink color for toast challenge completes
|
||||
String color = advancement.getDisplayData().getFrameType() == Advancement.DisplayData.FrameType.CHALLENGE ?
|
||||
"§d" : "§a";
|
||||
String advancementName = MessageTranslator.convertMessage(advancement.getDisplayData().getTitle(), session.getLocale());
|
||||
|
||||
// Send an action bar message stating they earned an achievement
|
||||
// Sent for instances where broadcasting advancements through chat are disabled
|
||||
SetTitlePacket titlePacket = new SetTitlePacket();
|
||||
titlePacket.setText(color + "[" + LocaleUtils.getLocaleString("advancements.toast." +
|
||||
advancement.getDisplayData().getFrameType().toString().toLowerCase(), session.getLocale()) + "]§f " + advancementName);
|
||||
titlePacket.setType(SetTitlePacket.Type.ACTIONBAR);
|
||||
titlePacket.setFadeOutTime(3);
|
||||
titlePacket.setFadeInTime(3);
|
||||
titlePacket.setStayTime(3);
|
||||
session.sendUpstreamPacket(titlePacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -217,8 +217,8 @@ public class FileUtils {
|
||||
* @return The byte array of the file
|
||||
*/
|
||||
public static byte[] readAllBytes(File file) {
|
||||
try {
|
||||
return readAllBytes(new FileInputStream(file));
|
||||
try (InputStream inputStream = new FileInputStream(file)) {
|
||||
return readAllBytes(inputStream);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Cannot read " + file);
|
||||
}
|
||||
|
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.utils;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.advancement.Advancement;
|
||||
import lombok.NonNull;
|
||||
import org.geysermc.connector.network.session.cache.AdvancementsCache;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A wrapper around MCProtocolLib's {@link Advancement} class so we can control the parent of an advancement
|
||||
*/
|
||||
public class GeyserAdvancement {
|
||||
private final Advancement advancement;
|
||||
private String rootId = null;
|
||||
|
||||
public static GeyserAdvancement from(Advancement advancement) {
|
||||
return new GeyserAdvancement(advancement);
|
||||
}
|
||||
|
||||
private GeyserAdvancement(Advancement advancement) {
|
||||
this.advancement = advancement;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getId() {
|
||||
return this.advancement.getId();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public List<String> getCriteria() {
|
||||
return this.advancement.getCriteria();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public List<List<String>> getRequirements() {
|
||||
return this.advancement.getRequirements();
|
||||
}
|
||||
|
||||
public String getParentId() {
|
||||
return this.advancement.getParentId();
|
||||
}
|
||||
|
||||
public Advancement.DisplayData getDisplayData() {
|
||||
return this.advancement.getDisplayData();
|
||||
}
|
||||
|
||||
public String getRootId(AdvancementsCache advancementsCache) {
|
||||
if (rootId == null) {
|
||||
if (this.advancement.getParentId() == null) {
|
||||
// We are the root ID
|
||||
this.rootId = this.advancement.getId();
|
||||
} else {
|
||||
// Go through our cache, and descend until we find the root ID
|
||||
GeyserAdvancement advancement = advancementsCache.getStoredAdvancements().get(this.advancement.getParentId());
|
||||
if (advancement.getParentId() == null) {
|
||||
this.rootId = advancement.getId();
|
||||
} else {
|
||||
this.rootId = advancement.getRootId(advancementsCache);
|
||||
}
|
||||
}
|
||||
}
|
||||
return rootId;
|
||||
}
|
||||
}
|
@ -142,9 +142,10 @@ public class LocaleUtils {
|
||||
try {
|
||||
File hashFile = GeyserConnector.getInstance().getBootstrap().getConfigFolder().resolve("locales/en_us.hash").toFile();
|
||||
if (hashFile.exists()) {
|
||||
BufferedReader br = new BufferedReader(new FileReader(hashFile));
|
||||
try (BufferedReader br = new BufferedReader(new FileReader(hashFile))) {
|
||||
curHash = br.readLine().trim();
|
||||
}
|
||||
}
|
||||
} catch (IOException ignored) { }
|
||||
targetHash = clientJarInfo.getSha1();
|
||||
} else {
|
||||
@ -208,6 +209,12 @@ public class LocaleUtils {
|
||||
|
||||
// Insert the locale into the mappings
|
||||
LOCALE_MAPPINGS.put(locale.toLowerCase(), langMap);
|
||||
|
||||
try {
|
||||
localeStream.close();
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError(LanguageUtils.getLocaleStringLog("geyser.locale.fail.file", locale, e.getMessage()));
|
||||
}
|
||||
} else {
|
||||
GeyserConnector.getInstance().getLogger().warning(LanguageUtils.getLocaleStringLog("geyser.locale.fail.missing", locale));
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 6f246c24ddbd543a359d651e706da470fe53ceeb
|
||||
Subproject commit 8141bc6aed878a95ed9ee3ca83a2381f9906c4b4
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren