-
Notifications
You must be signed in to change notification settings - Fork 518
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SpectateLogin implementation #2745
base: master
Are you sure you want to change the base?
Changes from 1 commit
5fd543c
a15a75d
164e270
ed3e52c
3cee02b
79b80c0
6196f07
b76ac81
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,7 @@ | |
import fr.xephi.authme.service.AntiBotService; | ||
import fr.xephi.authme.service.BukkitService; | ||
import fr.xephi.authme.service.JoinMessageService; | ||
import fr.xephi.authme.service.SpectateLoginService; | ||
import fr.xephi.authme.service.TeleportationService; | ||
import fr.xephi.authme.service.ValidationService; | ||
import fr.xephi.authme.settings.Settings; | ||
|
@@ -19,6 +20,7 @@ | |
import fr.xephi.authme.settings.properties.RegistrationSettings; | ||
import fr.xephi.authme.settings.properties.RestrictionSettings; | ||
import org.bukkit.ChatColor; | ||
import org.bukkit.GameMode; | ||
import org.bukkit.Location; | ||
import org.bukkit.entity.HumanEntity; | ||
import org.bukkit.entity.Player; | ||
|
@@ -49,6 +51,8 @@ | |
import org.bukkit.event.player.PlayerQuitEvent; | ||
import org.bukkit.event.player.PlayerRespawnEvent; | ||
import org.bukkit.event.player.PlayerShearEntityEvent; | ||
import org.bukkit.event.player.PlayerTeleportEvent; | ||
import org.bukkit.event.player.PlayerToggleSneakEvent; | ||
import org.bukkit.inventory.InventoryView; | ||
|
||
import javax.inject.Inject; | ||
|
@@ -91,6 +95,8 @@ public class PlayerListener implements Listener { | |
private PermissionsManager permissionsManager; | ||
@Inject | ||
private QuickCommandsProtectionManager quickCommandsProtectionManager; | ||
@Inject | ||
private SpectateLoginService spectateLoginService; | ||
|
||
// Lowest priority to apply fast protection checks | ||
@EventHandler(priority = EventPriority.LOWEST) | ||
|
@@ -376,6 +382,34 @@ public void onPlayerRespawn(PlayerRespawnEvent event) { | |
if (spawn != null && spawn.getWorld() != null) { | ||
event.setRespawnLocation(spawn); | ||
} | ||
|
||
if (settings.getProperty(RestrictionSettings.SPECTATE_STAND_LOGIN) | ||
|| spectateLoginService.hasStand(event.getPlayer())) { | ||
// If a player is dead, no stand will be created for him | ||
// therefore we can be sure that there will not be two stands | ||
bukkitService.runTaskLater(() -> spectateLoginService.createStand(event.getPlayer()), 1L); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can't confirm the comment about stands not being created if a player is dead but maybe I'm missing the relevant point, but interestingly the logic here specifies a stand should be created if the setting is enabled, or if the player has a stand? I don't think that makes much sense. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed unnecessary comment. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now an armor stand is created for a player every time he respawns, regardless of whether an armor stand already exists or not There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
} | ||
} | ||
|
||
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) | ||
public void onToggleSneak(PlayerToggleSneakEvent event) { | ||
if (listenerService.shouldCancelEvent(event.getPlayer()) | ||
&& (settings.getProperty(RestrictionSettings.SPECTATE_STAND_LOGIN) | ||
|| spectateLoginService.hasStand(event.getPlayer()))) { | ||
event.setCancelled(true); | ||
} | ||
} | ||
|
||
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) | ||
public void onTeleport(PlayerTeleportEvent event) { | ||
if (listenerService.shouldCancelEvent(event.getPlayer()) | ||
&& event.getCause() == PlayerTeleportEvent.TeleportCause.SPECTATE | ||
&& event.getPlayer().getGameMode() == GameMode.SPECTATOR | ||
&& (settings.getProperty(RestrictionSettings.SPECTATE_STAND_LOGIN) | ||
|| spectateLoginService.hasStand(event.getPlayer()))) { | ||
spectateLoginService.updateTarget(event.getPlayer()); | ||
event.setCancelled(true); | ||
} | ||
} | ||
|
||
/* | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
package fr.xephi.authme.service; | ||
|
||
import fr.xephi.authme.settings.properties.RestrictionSettings; | ||
import org.bukkit.GameMode; | ||
import org.bukkit.Location; | ||
import org.bukkit.entity.ArmorStand; | ||
import org.bukkit.entity.Player; | ||
import javax.inject.Inject; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
/** | ||
* Sets the player gamemode to Spectator, puts the player in an invisible armorstand and fixes the direction of the view | ||
*/ | ||
public class SpectateLoginService { | ||
|
||
private Map<Player, ArmorStand> armorStands = new HashMap<>(); | ||
private Map<Player, GameMode> gameModeMap = new HashMap<>(); | ||
|
||
@Inject | ||
private CommonService service; | ||
|
||
/** | ||
* Creates a stand for the player | ||
* | ||
* @param player the player | ||
*/ | ||
public void createStand(Player player) { | ||
if (player.isDead()) { | ||
return; | ||
} | ||
Location location = player.getLocation(); | ||
ArmorStand stand = spawnStand(location); | ||
|
||
armorStands.put(player, stand); | ||
gameModeMap.put(player, player.getGameMode()); | ||
|
||
player.setGameMode(GameMode.SPECTATOR); | ||
player.setSpectatorTarget(stand); | ||
} | ||
|
||
/** | ||
* Updates spectator target for the player | ||
* | ||
* @param player the player | ||
*/ | ||
public void updateTarget(Player player) { | ||
ArmorStand stand = armorStands.get(player); | ||
if (stand != null) { | ||
player.setSpectatorTarget(stand); | ||
} | ||
} | ||
|
||
/** | ||
* Removes the player's stand and deletes effects | ||
* | ||
* @param player the player | ||
*/ | ||
public void removeStand(Player player) { | ||
ArmorStand stand = armorStands.get(player); | ||
if (stand != null) { | ||
|
||
stand.remove(); | ||
player.setSpectatorTarget(null); | ||
player.setGameMode(gameModeMap.get(player)); | ||
|
||
gameModeMap.remove(player); | ||
armorStands.remove(player); | ||
} | ||
} | ||
|
||
/** | ||
* Removes all armorstands and restores player gamemode | ||
*/ | ||
public void removeArmorstands() { | ||
for (Player player : armorStands.keySet()) { | ||
removeStand(player); | ||
} | ||
|
||
gameModeMap.clear(); | ||
armorStands.clear(); | ||
} | ||
|
||
public boolean hasStand(Player player) { | ||
return armorStands.containsKey(player); | ||
} | ||
|
||
private ArmorStand spawnStand(Location loc) { | ||
double pitch = service.getProperty(RestrictionSettings.HEAD_PITCH); | ||
double yaw = service.getProperty(RestrictionSettings.HEAD_YAW); | ||
Location location = new Location(loc.getWorld(), loc.getX(), loc.getY(), loc.getBlockZ(), | ||
(float) yaw, (float) pitch); | ||
|
||
ArmorStand stand = location.getWorld().spawn(location, ArmorStand.class); | ||
|
||
stand.setGravity(false); | ||
stand.setAI(false); | ||
stand.setInvisible(true); | ||
|
||
return stand; | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -193,6 +193,22 @@ public final class RestrictionSettings implements SettingsHolder { | |||||
public static final Property<Set<String>> UNRESTRICTED_INVENTORIES = | ||||||
newLowercaseStringSetProperty("settings.unrestrictions.UnrestrictedInventories"); | ||||||
|
||||||
@Comment({ | ||||||
"While in unregistered/logged out state, should players be set to spectator mode and", | ||||||
"forced to spectate within a spawn point invisible armor stand, for 0 movement and head", | ||||||
"pitch + yaw? may be more effective than 'allowMovement' at locking the player in place." | ||||||
}) | ||||||
public static final Property<Boolean> SPECTATE_STAND_LOGIN = | ||||||
newProperty("settings.restrictions.spectateStandLogin.enabled", false); | ||||||
|
||||||
@Comment("Head Yaw position for 'spectateStandLogin'.") | ||||||
public static final Property<Double> HEAD_YAW = | ||||||
newProperty("settings.restrictions.spectateStandLogin.headYaw", 0.0f); | ||||||
|
||||||
@Comment("Head Pitch position for 'spectateStandLogin'.") | ||||||
public static final Property<Double> HEAD_PITCH = | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this should work with the version of ConfigMe that AuthMe is using (please ignore this suggestion if it doesn't)
Suggested change
Also the naming is a bit weird, I don't know that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know If i understood you correctly, but There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for changing the comment. I also meant the field name, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Renamed. |
||||||
newProperty("settings.restrictions.spectateStandLogin.headPitch", 0.0f); | ||||||
|
||||||
private RestrictionSettings() { | ||||||
} | ||||||
|
||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential null pointer if the plugin crashes before this service is set on L251
The proper way to do this is to not have the field, but to use another method from the injector (not sure what methods exist by heart, but if there's something that returns a singleton if it's been registered, that would be it, otherwise maybe
injector.createIfHasDependencies
though it's not ideal to still potentially create it at this point)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
injector.createIfHasDependencies(SpectateLoginService.class)
might return null, so the issue of a potential null pointer still remains. And I think it would be better to useinjector#getIfAvailable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.