Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 78 additions & 34 deletions src/main/java/betterquesting/client/gui2/party/GuiPartyManage.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@
import net.minecraft.nbt.NBTTagCompound;
import org.lwjgl.input.Keyboard;

import java.util.List;
import java.util.UUID;
import java.util.*;
import java.util.stream.Collectors;

public class GuiPartyManage extends GuiScreenCanvas implements IPEventListener, INeedsRefresh {
private IParty party;
Expand All @@ -56,6 +56,13 @@ public GuiPartyManage(GuiScreen parent) {
super(parent);
}

// 0 = INVITE, 1 = MEMBER, 2 = ADMIN, 3 = OWNER/OP
private EnumPartyStatus getStatus(UUID id) {
if (NameCache.INSTANCE.isOP(id)) return EnumPartyStatus.OWNER;
EnumPartyStatus perm = party.getStatus(id);
return perm == null ? EnumPartyStatus.MEMBER : perm; // Fallback (potentially exploitable I know)
}

@Override
public void refreshGui() {
UUID playerID = QuestingAPI.getQuestingUUID(mc.player);
Expand Down Expand Up @@ -94,9 +101,7 @@ public void initPanel() {
PEventBroadcaster.INSTANCE.register(this, PEventButton.class);
Keyboard.enableRepeatEvents(true);

// 0 = INVITE, 1 = MEMBER, 2 = ADMIN, 3 = OWNER/OP
EnumPartyStatus status = NameCache.INSTANCE.isOP(playerID) ? EnumPartyStatus.OWNER : party.getStatus(playerID);
if (status == null) status = EnumPartyStatus.MEMBER; // Fallback (potentially exploitable I know)
EnumPartyStatus status = getStatus(playerID);

// Background panel
CanvasTextured cvBackground = new CanvasTextured(new GuiTransform(GuiAlign.FULL_BOX, new GuiPadding(0, 0, 0, 0), 0), PresetTexture.PANEL_MAIN.getTexture());
Expand Down Expand Up @@ -140,10 +145,6 @@ public void initPanel() {
CanvasEmpty cvRightHalf = new CanvasEmpty(new GuiTransform(GuiAlign.HALF_RIGHT, new GuiPadding(8, 32, 16, 32), 0));
cvBackground.addPanel(cvRightHalf);

PanelTextBox txInvite = new PanelTextBox(new GuiTransform(GuiAlign.TOP_EDGE, new GuiPadding(0, 0, 0, -16), 0), QuestTranslation.translate("betterquesting.gui.party_members")).setAlignment(1);
txInvite.setColor(PresetColor.TEXT_HEADER.getColor());
cvRightHalf.addPanel(txInvite);

CanvasScrolling cvUserList = new CanvasScrolling(new GuiTransform(GuiAlign.FULL_BOX, new GuiPadding(0, 16, 8, 0), 0));
cvRightHalf.addPanel(cvUserList);

Expand All @@ -153,44 +154,71 @@ public void initPanel() {
scUserList.getTransform().setParent(cvUserList.getTransform());
cvUserList.setScrollDriverY(scUserList);

List<UUID> partyMemList = party.getMembers();
int elSize = RenderUtils.getStringWidth("...", fontRenderer);
int cvWidth = cvUserList.getTransform().getWidth();
boolean hardcore = QuestSettings.INSTANCE.getProperty(NativeProps.HARDCORE);
ItemTexture txHeart = new ItemTexture(new BigItemStack(BetterQuesting.extraLife));

for (int i = 0; i < partyMemList.size(); i++) {
UUID mid = partyMemList.get(i);
String mName = NameCache.INSTANCE.getName(mid);
int heightOffset = 0;

if (RenderUtils.getStringWidth(mName, fontRenderer) > cvWidth - 58) {
mName = mc.fontRenderer.trimStringToWidth(mName, cvWidth - 58 - elSize) + "...";
}
// Iterate through the statuses in descending order
for (EnumPartyStatus statusEntry : Arrays.asList(EnumPartyStatus.OWNER, EnumPartyStatus.ADMIN, EnumPartyStatus.MEMBER)) {
List<UUID> members = party.getMembers().stream().filter(id -> getStatus(id).equals(statusEntry)).collect(Collectors.toList());

PanelPlayerPortrait pnPortrait = new PanelPlayerPortrait(new GuiRectangle(0, i * 32, 32, 32, 0), mid, mName);
cvUserList.addPanel(pnPortrait);
if (members.isEmpty()) continue;

PanelTextBox txMemName = new PanelTextBox(new GuiRectangle(32, i * 32 + 4, cvWidth - 32, 12, 0), mName);
txMemName.setColor(PresetColor.TEXT_MAIN.getColor());
cvUserList.addPanel(txMemName);
String statusNameKey = switch (statusEntry) {
case OWNER -> "betterquesting.gui.party_owner";
case ADMIN -> "betterquesting.gui.party_admins";
case MEMBER -> "betterquesting.gui.party_members";
};

PanelButtonStorage<String> btnKick = new PanelButtonStorage<>(new GuiRectangle(cvWidth - 32, i * 32, 32, 32, 0), 3, QuestTranslation.translate("betterquesting.btn.party_kick"), mName);
cvUserList.addPanel(btnKick);
PanelTextBox txStatusName = new PanelTextBox(new GuiRectangle(0, heightOffset, cvWidth, 16, 0), QuestTranslation.translate(statusNameKey)).setAlignment(1);
txStatusName.setColor(PresetColor.TEXT_HEADER.getColor());
cvUserList.addPanel(txStatusName);

PanelGeneric pnItem = new PanelGeneric(new GuiRectangle(32, i * 32 + 16, 16, 16, 0), txHeart);
cvUserList.addPanel(pnItem);
heightOffset += 16;

String lifeCount;
for (UUID id : members) {
String username = NameCache.INSTANCE.getName(id);
boolean displayKickButton = statusEntry.ordinal() < status.ordinal() && playerID != id;
boolean displayAdminButton = displayKickButton && status == EnumPartyStatus.OWNER;

if (hardcore) {
lifeCount = " x " + LifeDatabase.INSTANCE.getLives(mid);
} else {
lifeCount = " x \u221E";
}
PanelPlayerPortrait pnPortrait = new PanelPlayerPortrait(new GuiRectangle(0, heightOffset, 32, 32, 0), id, username);
cvUserList.addPanel(pnPortrait);

int maxAllowedWidth = cvWidth - 32;
if (displayKickButton) maxAllowedWidth -= 32;
if (displayAdminButton) maxAllowedWidth -= 48;

PanelTextBox txLives = new PanelTextBox(new GuiRectangle(48, i * 32 + 20, cvWidth - 48 - 32, 12, 0), lifeCount);
txLives.setColor(PresetColor.TEXT_MAIN.getColor());
cvUserList.addPanel(txLives);
String shortenedName = RenderUtils.getStringWidth(username, fontRenderer) > maxAllowedWidth ? fontRenderer.trimStringToWidth(username, maxAllowedWidth - elSize) + "..." : username;
PanelTextBox txMemName = new PanelTextBox(new GuiRectangle(32, heightOffset + 4, cvWidth - 32, 12, 0), shortenedName);
txMemName.setColor(PresetColor.TEXT_MAIN.getColor());
cvUserList.addPanel(txMemName);

if (displayKickButton) {
PanelButtonStorage<String> btnKick = new PanelButtonStorage<>(new GuiRectangle(cvWidth - 32, heightOffset, 32, 32, 0), 3, QuestTranslation.translate("betterquesting.btn.party_kick"), username);
cvUserList.addPanel(btnKick);

if (displayAdminButton) {
var rect = new GuiRectangle(cvWidth - 80, heightOffset, 48, 32, 0);
if (statusEntry == EnumPartyStatus.MEMBER) {
cvUserList.addPanel(new PanelButtonStorage<>(rect, 5, QuestTranslation.translate("betterquesting.btn.party_promote"), username));
} else {
cvUserList.addPanel(new PanelButtonStorage<>(rect, 6, QuestTranslation.translate("betterquesting.btn.party_demote"), username));
}
}
}

PanelGeneric pnItem = new PanelGeneric(new GuiRectangle(32, heightOffset + 16, 16, 16, 0), txHeart);
cvUserList.addPanel(pnItem);

PanelTextBox txLives = new PanelTextBox(new GuiRectangle(48, heightOffset + 20, cvWidth - 48 - 32, 12, 0), " x " + (hardcore ? LifeDatabase.INSTANCE.getLives(id) : "∞"));
txLives.setColor(PresetColor.TEXT_MAIN.getColor());
cvUserList.addPanel(txLives);

heightOffset += 32;
}
}

scUserList.setActive(cvUserList.getScrollBounds().getHeight() > 0);
Expand Down Expand Up @@ -238,6 +266,22 @@ private void onButtonPress(PEventButton event) {
payload.setInteger("partyID", partyID);
payload.setTag("data", party.writeProperties(new NBTTagCompound()));
NetPartyAction.sendAction(payload);
} else if (btn.getButtonID() == 5 && btn instanceof PanelButtonStorage) // Toggle permission level of user between ADMIN and MEMBER
{
String id = ((PanelButtonStorage<String>) btn).getStoredValue();
NBTTagCompound payload = new NBTTagCompound();
payload.setInteger("action", 6);
payload.setInteger("partyID", partyID);
payload.setString("username", id);
NetPartyAction.sendAction(payload);
} else if (btn.getButtonID() == 6 && btn instanceof PanelButtonStorage) // Toggle permission level of user between ADMIN and MEMBER
{
String id = ((PanelButtonStorage<String>) btn).getStoredValue();
NBTTagCompound payload = new NBTTagCompound();
payload.setInteger("action", 7);
payload.setInteger("partyID", partyID);
payload.setString("username", id);
NetPartyAction.sendAction(payload);
}
}
}
115 changes: 88 additions & 27 deletions src/main/java/betterquesting/network/handlers/NetPartyAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import org.apache.logging.log4j.Level;

import java.util.UUID;

Expand All @@ -46,16 +45,17 @@ public static void sendAction(NBTTagCompound payload) {
}

private static void onServer(Tuple<NBTTagCompound, EntityPlayerMP> message) {
NBTTagCompound compound = message.getFirst();
EntityPlayerMP sender = message.getSecond();

int action = !message.getFirst().hasKey("action", 99) ? -1 : message.getFirst().getInteger("action");
int partyID = !message.getFirst().hasKey("partyID", 99) ? -1 : message.getFirst().getInteger("partyID");
int action = !compound.hasKey("action", 99) ? -1 : compound.getInteger("action");
int partyID = !compound.hasKey("partyID", 99) ? -1 : compound.getInteger("partyID");
IParty party = PartyManager.INSTANCE.getValue(partyID);
int permission = party == null ? 0 : checkPermission(QuestingAPI.getQuestingUUID(sender), party);

switch (action) {
case 0: {
createParty(sender, message.getFirst().getString("name"));
createParty(sender, compound.getString("name"));
break;
}
case 1: {
Expand All @@ -65,28 +65,43 @@ private static void onServer(Tuple<NBTTagCompound, EntityPlayerMP> message) {
}
case 2: {
if (permission < 2) break;
editParty(partyID, party, message.getFirst().getCompoundTag("data"));
editParty(partyID, party, compound.getCompoundTag("data"));
break;
}
case 3: {
if (permission < 2) break;
inviteUser(partyID, message.getFirst().getString("username"), message.getFirst().getLong("expiry"));
inviteUser(partyID, compound.getString("username"), compound.getLong("expiry"));
break;
}
case 4: {
acceptInvite(partyID, sender); // Probably the only thing an OP can't force
break;
}
case 5: {
kickUser(partyID, sender, party, message.getFirst().getString("username"), permission);
kickUser(partyID, sender, party, compound.getString("username"), permission);
break;
}
case 6: {
promotePlayer(partyID, party, compound.getString("username"), permission);
break;
}
case 7: {
demotePlayer(partyID, party, compound.getString("username"), permission);
break;
}
default: {
BetterQuesting.logger.log(Level.ERROR, "Invalid party action '" + action + "'. Full payload:\n" + message.getFirst().toString());
BetterQuesting.logger.error("Invalid party action '" + action + "'. Full payload:\n" + compound);
}
}
}

private static UUID getUUID(String username) {
MinecraftServer server = FMLCommonHandler.instance().getMinecraftServerInstance();
EntityPlayerMP player = server.getPlayerList().getPlayerByUsername(username);
UUID uuid = QuestingAPI.getQuestingUUID(player);
return uuid == null ? NameCache.INSTANCE.getUUID(username) : uuid;
}

private static void createParty(EntityPlayerMP sender, String name) {
UUID playerID = QuestingAPI.getQuestingUUID(sender);
if (PartyManager.INSTANCE.getParty(playerID) != null) return;
Expand Down Expand Up @@ -114,19 +129,18 @@ private static void editParty(int partyID, IParty party, NBTTagCompound settings
}

private static void inviteUser(int partyID, String username, long expiry) {
UUID uuid = null;
MinecraftServer server = FMLCommonHandler.instance().getMinecraftServerInstance();
EntityPlayerMP player = server.getPlayerList().getPlayerByUsername(username);
if (player != null) uuid = QuestingAPI.getQuestingUUID(player);
if (uuid == null) uuid = NameCache.INSTANCE.getUUID(username);
if (uuid != null) {
PartyInvitations.INSTANCE.postInvite(uuid, partyID, expiry);
if (player != null) {
NetPartySync.sendSync(new EntityPlayerMP[]{player}, new int[]{partyID});
NetInviteSync.sendSync(player);
}
} else {
BetterQuesting.logger.error("Unable to identify " + username + " to invite to party " + partyID); // No idea who this is
UUID uuid = getUUID(username);
if (uuid == null) {
BetterQuesting.logger.error("Unable to identify " + username + " to invite to party " + partyID);
return; // No idea who this is
}
PartyInvitations.INSTANCE.postInvite(uuid, partyID, expiry);
// despite what the annotations claim, this can return null
var player = FMLCommonHandler.instance().getMinecraftServerInstance().getPlayerList().getPlayerByUUID(uuid);
//noinspection ConstantValue
if (player != null) {
NetPartySync.sendSync(new EntityPlayerMP[]{player}, new int[]{partyID});
NetInviteSync.sendSync(player);
}
}

Expand All @@ -136,7 +150,7 @@ private static void acceptInvite(int partyID, EntityPlayerMP sender) {
if (party != null) return;
if (PartyInvitations.INSTANCE.acceptInvite(playerID, partyID)) {
NetPartySync.quickSync(partyID);
NetNameSync.quickSync(sender, partyID);
NetNameSync.quickSync(null, partyID);
} else {
BetterQuesting.logger.error("Invalid invite for " + sender.getName() + " to party " + partyID);
}
Expand All @@ -150,11 +164,7 @@ private static void kickUser(int partyID, EntityPlayerMP sender, IParty party, S
return;
}

UUID uuid = null;
MinecraftServer server = FMLCommonHandler.instance().getMinecraftServerInstance();
EntityPlayerMP player = server.getPlayerList().getPlayerByUsername(username);
if (player != null) uuid = QuestingAPI.getQuestingUUID(player);
if (uuid == null) uuid = NameCache.INSTANCE.getUUID(username);
UUID uuid = getUUID(username);
if (uuid == null) {
BetterQuesting.logger.error("Unable to identify " + username + " to remove them from party " + partyID);
return; // No idea who this is
Expand All @@ -167,6 +177,9 @@ private static void kickUser(int partyID, EntityPlayerMP sender, IParty party, S

if (party.getMembers().size() > 0) {
NetPartySync.quickSync(partyID);
// despite what the annotations claim, this can return null
var player = FMLCommonHandler.instance().getMinecraftServerInstance().getPlayerList().getPlayerByUUID(uuid);
//noinspection ConstantValue
if (player != null) {
NBTTagCompound payload = new NBTTagCompound();
payload.setInteger("action", 5);
Expand All @@ -188,6 +201,54 @@ private static void kickUser(int partyID, EntityPlayerMP sender, IParty party, S
}
}

private static void promotePlayer(int partyID, IParty party, String username, int permission) {
if (permission < EnumPartyStatus.ADMIN.ordinal()) {
BetterQuesting.logger.error("Tried to promote a player, but didn't have a high enough permission to do so!");
return;
}
if (party == null) {
BetterQuesting.logger.error("Tried to increase a player's permission level of a non-existent party (" + partyID + ")");
return;
}

UUID uuid = getUUID(username);
if (uuid == null) {
BetterQuesting.logger.error("Unable to identify " + username + " to promote them in " + partyID);
return; // No idea who this is
}

if (checkPermission(uuid, party) < permission) {
party.setStatus(uuid, EnumPartyStatus.ADMIN);
NetPartySync.quickSync(partyID);
} else {
BetterQuesting.logger.error("Insufficient permissions to increase the status level of " + username + " from party " + partyID);
}
}

private static void demotePlayer(int partyID, IParty party, String username, int permission) {
if (permission < EnumPartyStatus.OWNER.ordinal()) {
BetterQuesting.logger.error("Tried to demote a player, but didn't have a high enough permission to do so!");
return;
}
if (party == null) {
BetterQuesting.logger.error("Tried to decrease a player's permission level of a non-existent party (" + partyID + ")");
return;
}

UUID uuid = getUUID(username);
if (uuid == null) {
BetterQuesting.logger.error("Unable to identify " + username + " to demote them in " + partyID);
return; // No idea who this is
}

if (checkPermission(uuid, party) < permission) {
party.setStatus(uuid, EnumPartyStatus.MEMBER);
NetPartySync.quickSync(partyID);
} else {
BetterQuesting.logger.error("Insufficient permissions to decrease the status level of " + username + " from party " + partyID);
}
}

private static int checkPermission(UUID playerID, IParty party) {
MinecraftServer server = FMLCommonHandler.instance().getMinecraftServerInstance();
EntityPlayerMP player = server == null ? null : server.getPlayerList().getPlayerByUUID(playerID);
Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/assets/betterquesting/lang/en_us.lang
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ betterquesting.btn.party_new=Create Party
betterquesting.btn.party_leave=Leave
betterquesting.btn.party_invite=Invite
betterquesting.btn.party_kick=Kick
betterquesting.btn.party_promote=Promote
betterquesting.btn.party_demote=Demote
betterquesting.btn.party_join=Join
betterquesting.btn.party_share_lives=Share Lives
betterquesting.btn.party_share_loot=Single Reward
Expand Down Expand Up @@ -83,6 +85,8 @@ betterquesting.gui.search=Search
betterquesting.gui.folder=Folder
betterquesting.gui.party_invites=Invites
betterquesting.gui.party_members=Members
betterquesting.gui.party_admins=Admins
betterquesting.gui.party_owner=Owner
betterquesting.gui.remaining_lives=Remaining Lives: %s
betterquesting.gui.full_lives=Lives Full
betterquesting.gui.closing_warning=Are you sure you want to close this editor?
Expand Down
Loading