2021-01-12 02:23:09 +00:00
|
|
|
/*
|
2022-01-01 19:03:05 +00:00
|
|
|
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
2021-01-12 02:23:09 +00:00
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
|
2021-11-20 23:29:46 +00:00
|
|
|
package org.geysermc.geyser.session.cache;
|
2021-01-12 02:23:09 +00:00
|
|
|
|
|
|
|
import com.github.steveice10.mc.protocol.data.game.advancement.Advancement;
|
2021-11-14 05:07:24 +00:00
|
|
|
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.inventory.ServerboundSeenAdvancementsPacket;
|
2021-01-12 02:23:09 +00:00
|
|
|
import lombok.Getter;
|
|
|
|
import lombok.Setter;
|
2022-01-22 11:20:52 +00:00
|
|
|
import org.geysermc.cumulus.form.SimpleForm;
|
2021-11-20 23:29:46 +00:00
|
|
|
import org.geysermc.geyser.level.GeyserAdvancement;
|
2021-11-22 19:52:26 +00:00
|
|
|
import org.geysermc.geyser.session.GeyserSession;
|
2022-04-23 00:16:22 +00:00
|
|
|
import org.geysermc.geyser.text.ChatColor;
|
2021-11-20 23:29:46 +00:00
|
|
|
import org.geysermc.geyser.text.GeyserLocale;
|
|
|
|
import org.geysermc.geyser.text.MinecraftLocale;
|
2022-01-22 11:20:52 +00:00
|
|
|
import org.geysermc.geyser.translator.text.MessageTranslator;
|
2021-01-12 02:23:09 +00:00
|
|
|
|
2022-05-26 06:11:25 +00:00
|
|
|
import java.util.ArrayList;
|
2021-01-12 02:23:09 +00:00
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.Map;
|
|
|
|
|
|
|
|
public class AdvancementsCache {
|
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2021-06-05 22:53:58 +00:00
|
|
|
@Setter
|
2021-01-12 02:23:09 +00:00
|
|
|
private String currentAdvancementCategoryId = null;
|
|
|
|
|
2021-11-22 19:52:26 +00:00
|
|
|
private final GeyserSession session;
|
2021-01-12 02:23:09 +00:00
|
|
|
|
2021-11-22 19:52:26 +00:00
|
|
|
public AdvancementsCache(GeyserSession session) {
|
2021-01-12 02:23:09 +00:00
|
|
|
this.session = session;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-02-01 23:46:46 +00:00
|
|
|
* Build and send a form with all advancement categories
|
2021-01-12 02:23:09 +00:00
|
|
|
*/
|
2021-02-01 23:46:46 +00:00
|
|
|
public void buildAndShowMenuForm() {
|
|
|
|
SimpleForm.Builder builder =
|
|
|
|
SimpleForm.builder()
|
2022-01-16 21:09:53 +00:00
|
|
|
.translator(MinecraftLocale::getLocaleString, session.locale())
|
2021-02-01 23:46:46 +00:00
|
|
|
.title("gui.advancements");
|
2021-01-12 02:23:09 +00:00
|
|
|
|
2022-05-26 06:11:25 +00:00
|
|
|
List<String> rootAdvancementIds = new ArrayList<>();
|
2021-01-12 02:23:09 +00:00
|
|
|
for (Map.Entry<String, GeyserAdvancement> advancement : storedAdvancements.entrySet()) {
|
|
|
|
if (advancement.getValue().getParentId() == null) { // No parent means this is a root advancement
|
2022-01-16 21:09:53 +00:00
|
|
|
builder.button(MessageTranslator.convertMessage(advancement.getValue().getDisplayData().getTitle(), session.locale()));
|
2022-05-26 06:11:25 +00:00
|
|
|
rootAdvancementIds.add(advancement.getKey());
|
2021-01-12 02:23:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-26 06:11:25 +00:00
|
|
|
if (rootAdvancementIds.isEmpty()) {
|
2021-02-01 23:46:46 +00:00
|
|
|
builder.content("advancements.empty");
|
2021-01-12 02:23:09 +00:00
|
|
|
}
|
|
|
|
|
2022-05-29 21:39:40 +00:00
|
|
|
builder.validResultHandler((response) -> {
|
2022-06-06 22:14:43 +00:00
|
|
|
String id = rootAdvancementIds.get(response.clickedButtonId());
|
2021-02-01 23:46:46 +00:00
|
|
|
if (!id.equals("")) {
|
|
|
|
if (id.equals(currentAdvancementCategoryId)) {
|
|
|
|
// The server thinks we are already on this tab
|
|
|
|
buildAndShowListForm();
|
|
|
|
} else {
|
|
|
|
// Send a packet indicating that we intend to open this particular advancement window
|
2021-11-13 03:44:15 +00:00
|
|
|
ServerboundSeenAdvancementsPacket packet = new ServerboundSeenAdvancementsPacket(id);
|
2023-10-12 13:02:57 +00:00
|
|
|
session.sendDownstreamGamePacket(packet);
|
2021-02-01 23:46:46 +00:00
|
|
|
// Wait for a response there
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
session.sendForm(builder);
|
2021-01-12 02:23:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-02-01 23:46:46 +00:00
|
|
|
* Build and send the list of advancements
|
2021-01-12 02:23:09 +00:00
|
|
|
*/
|
2021-02-01 23:46:46 +00:00
|
|
|
public void buildAndShowListForm() {
|
2021-01-12 02:23:09 +00:00
|
|
|
GeyserAdvancement categoryAdvancement = storedAdvancements.get(currentAdvancementCategoryId);
|
2022-01-16 21:09:53 +00:00
|
|
|
String language = session.locale();
|
2021-01-12 02:23:09 +00:00
|
|
|
|
2021-02-01 23:46:46 +00:00
|
|
|
SimpleForm.Builder builder =
|
|
|
|
SimpleForm.builder()
|
|
|
|
.title(MessageTranslator.convertMessage(categoryAdvancement.getDisplayData().getTitle(), language))
|
|
|
|
.content(MessageTranslator.convertMessage(categoryAdvancement.getDisplayData().getDescription(), language));
|
2021-01-12 02:23:09 +00:00
|
|
|
|
2022-05-26 06:11:25 +00:00
|
|
|
List<GeyserAdvancement> visibleAdvancements = new ArrayList<>();
|
2021-02-01 23:46:46 +00:00
|
|
|
if (currentAdvancementCategoryId != null) {
|
|
|
|
for (GeyserAdvancement advancement : storedAdvancements.values()) {
|
2022-05-26 06:11:25 +00:00
|
|
|
boolean earned = isEarned(advancement);
|
|
|
|
if (earned || !advancement.getDisplayData().isHidden()) {
|
2021-01-12 02:23:09 +00:00
|
|
|
if (advancement.getParentId() != null && currentAdvancementCategoryId.equals(advancement.getRootId(this))) {
|
2022-05-26 06:11:25 +00:00
|
|
|
String color = earned ? advancement.getDisplayColor() : "";
|
|
|
|
builder.button(color + MessageTranslator.convertMessage(advancement.getDisplayData().getTitle()) + '\n');
|
|
|
|
|
|
|
|
visibleAdvancements.add(advancement);
|
2021-01-12 02:23:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-20 23:29:46 +00:00
|
|
|
builder.button(GeyserLocale.getPlayerLocaleString("gui.back", language));
|
2021-01-12 02:23:09 +00:00
|
|
|
|
2022-05-29 21:39:40 +00:00
|
|
|
builder.closedResultHandler(() -> {
|
2022-01-22 11:20:52 +00:00
|
|
|
// Indicate that we have closed the current advancement tab
|
2023-10-12 13:02:57 +00:00
|
|
|
session.sendDownstreamGamePacket(new ServerboundSeenAdvancementsPacket());
|
2021-02-01 23:46:46 +00:00
|
|
|
|
2022-05-29 21:39:40 +00:00
|
|
|
}).validResultHandler((response) -> {
|
2023-12-05 23:54:42 +00:00
|
|
|
if (response.clickedButtonId() < visibleAdvancements.size()) {
|
2022-06-06 22:14:43 +00:00
|
|
|
GeyserAdvancement advancement = visibleAdvancements.get(response.clickedButtonId());
|
2021-02-01 23:46:46 +00:00
|
|
|
buildAndShowInfoForm(advancement);
|
|
|
|
} else {
|
|
|
|
buildAndShowMenuForm();
|
|
|
|
// Indicate that we have closed the current advancement tab
|
2023-10-12 13:02:57 +00:00
|
|
|
session.sendDownstreamGamePacket(new ServerboundSeenAdvancementsPacket());
|
2021-02-01 23:46:46 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
session.sendForm(builder);
|
2021-01-12 02:23:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Builds the advancement display info based on the chosen category
|
|
|
|
*
|
|
|
|
* @param advancement The advancement used to create the info display
|
|
|
|
*/
|
2021-02-01 23:46:46 +00:00
|
|
|
public void buildAndShowInfoForm(GeyserAdvancement advancement) {
|
2021-01-12 02:23:09 +00:00
|
|
|
// Cache language for easier access
|
2022-01-16 21:09:53 +00:00
|
|
|
String language = session.locale();
|
2021-01-12 02:23:09 +00:00
|
|
|
|
|
|
|
String earned = isEarned(advancement) ? "yes" : "no";
|
|
|
|
|
|
|
|
String description = getColorFromAdvancementFrameType(advancement) + MessageTranslator.convertMessage(advancement.getDisplayData().getDescription(), language);
|
2021-11-20 23:29:46 +00:00
|
|
|
String earnedString = GeyserLocale.getPlayerLocaleString("geyser.advancements.earned", language, MinecraftLocale.getLocaleString("gui." + earned, language));
|
2021-01-12 02:23:09 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
Layout will look like:
|
|
|
|
|
|
|
|
(Form title) Stone Age
|
|
|
|
|
|
|
|
(Description) Mine stone with your new pickaxe
|
|
|
|
|
|
|
|
Earned: Yes
|
|
|
|
Parent Advancement: Minecraft // If relevant
|
|
|
|
*/
|
|
|
|
|
2021-02-01 23:46:46 +00:00
|
|
|
String content = description + "\n\n§f" + earnedString + "\n";
|
2021-01-12 02:23:09 +00:00
|
|
|
if (!currentAdvancementCategoryId.equals(advancement.getParentId())) {
|
|
|
|
// Only display the parent if it is not the category
|
2021-11-20 23:29:46 +00:00
|
|
|
content += GeyserLocale.getPlayerLocaleString("geyser.advancements.parentid", language, MessageTranslator.convertMessage(storedAdvancements.get(advancement.getParentId()).getDisplayData().getTitle(), language));
|
2021-01-12 02:23:09 +00:00
|
|
|
}
|
|
|
|
|
2021-02-01 23:46:46 +00:00
|
|
|
session.sendForm(
|
|
|
|
SimpleForm.builder()
|
|
|
|
.title(MessageTranslator.convertMessage(advancement.getDisplayData().getTitle()))
|
|
|
|
.content(content)
|
2021-11-20 23:29:46 +00:00
|
|
|
.button(GeyserLocale.getPlayerLocaleString("gui.back", language))
|
2022-05-29 21:39:40 +00:00
|
|
|
.validResultHandler((response) -> buildAndShowListForm())
|
2021-02-01 23:46:46 +00:00
|
|
|
);
|
2021-01-12 02:23:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
public String getColorFromAdvancementFrameType(GeyserAdvancement advancement) {
|
2023-11-25 12:09:02 +00:00
|
|
|
if (advancement.getDisplayData().getAdvancementType() == Advancement.DisplayData.AdvancementType.CHALLENGE) {
|
2022-04-23 00:16:22 +00:00
|
|
|
return ChatColor.DARK_PURPLE;
|
2021-01-12 02:23:09 +00:00
|
|
|
}
|
2022-04-23 00:16:22 +00:00
|
|
|
return ChatColor.GREEN; // Used for types TASK and GOAL
|
2021-01-12 02:23:09 +00:00
|
|
|
}
|
|
|
|
}
|