mirror of
https://github.com/Plugily-Projects/BuildBattle.git
synced 2026-11-05 00:50:03 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66e556003e | ||
|
|
a3e590ce07 | ||
|
|
a81d21d4ec | ||
|
|
bc575c26d2 | ||
|
|
4fb500c015 | ||
|
|
86202128d2 |
No files matched your search
+17
-1
@@ -10,6 +10,22 @@ deploy webhooks to print changelog successfully
|
||||
|
||||
That's all, matcher will stop when detects next line started with `###` match
|
||||
|
||||
### 5.0.9 Release (09.07.2024)
|
||||
* Updated to minigamesbox 1.3.11
|
||||
* Fixed ChunkManager.sendMapChunk
|
||||
* Fixed error in time change menu
|
||||
* Fixed issue with %player% placeholder (should be lowercase)
|
||||
* Fixed problem in place rewards
|
||||
* Fixed small error if non-player threw projectile.
|
||||
* Added Cancel of BlockExplodeEvent to prevent blocks such as Respawn Anchor explode and damage the arena
|
||||
* Added Disabled enderpearl throwing while in arena because it allows to teleport to other players plots or outside the arena map, even during voting stage.
|
||||
|
||||
### 5.0.8 Release (25.06.2024)
|
||||
* Updated to minigamesbox 1.3.9
|
||||
|
||||
### 5.0.7 Release (18.06.2024)
|
||||
* API update
|
||||
|
||||
### 5.0.6 Release (24.04.2024)
|
||||
* Updated to minigamesbox 1.3.8
|
||||
|
||||
@@ -630,4 +646,4 @@ it will have BEDROCK value if absent
|
||||
|
||||
### 3.0.0 Release (14.05.2018 - 01.06.2018)
|
||||
|
||||
* Add everything here
|
||||
* Add everything here
|
||||
+2
-2
@@ -39,7 +39,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("plugily.projects:MiniGamesBox-Classic:1.3.9") { isTransitive = false }
|
||||
implementation("plugily.projects:MiniGamesBox-Classic:1.3.12") { isTransitive = false }
|
||||
compileOnly("org.spigotmc:spigot-api:1.20.4-R0.1-SNAPSHOT")
|
||||
compileOnly("io.papermc.paper:paper-api:1.20.4-R0.1-SNAPSHOT")
|
||||
compileOnly("net.citizensnpcs:citizensapi:2.0.31-SNAPSHOT")
|
||||
@@ -47,7 +47,7 @@ dependencies {
|
||||
}
|
||||
|
||||
group = "plugily.projects"
|
||||
version = "5.0.7"
|
||||
version = "5.0.10"
|
||||
description = "BuildBattle"
|
||||
|
||||
java {
|
||||
|
||||
@@ -418,6 +418,8 @@ public class ArenaEvents extends PluginArenaEvents {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGH)
|
||||
public void onDamage(EntityDamageEvent event) {
|
||||
if(event.getEntity().getType() != EntityType.PLAYER) {
|
||||
@@ -468,6 +470,36 @@ public class ArenaEvents extends PluginArenaEvents {
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onOtherBlockExplode(BlockExplodeEvent event) {
|
||||
Location blockLocation = event.getBlock().getLocation();
|
||||
|
||||
for(IPluginArena arena : plugin.getArenaRegistry().getArenas()) {
|
||||
if(!(arena instanceof BaseArena)) {
|
||||
continue;
|
||||
}
|
||||
for(Plot buildPlot : ((BaseArena) arena).getPlotManager().getPlots()) {
|
||||
if(buildPlot.getCuboid() != null && buildPlot.getCuboid().isInWithMarge(blockLocation, 5)) {
|
||||
event.blockList().clear();
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onEnderpearlThrow(ProjectileLaunchEvent event) {
|
||||
if(event.getEntity().getShooter() instanceof Player) {
|
||||
BaseArena arena = plugin.getArenaRegistry().getArena((Player) event.getEntity().getShooter());
|
||||
if (arena == null || arena.getArenaState() != IArenaState.IN_GAME) {
|
||||
return;
|
||||
}
|
||||
if (event.getEntity() instanceof EnderPearl) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onWaterFlowEvent(BlockFromToEvent event) {
|
||||
Location toBlock = event.getToBlock().getLocation();
|
||||
@@ -532,15 +564,6 @@ public class ArenaEvents extends PluginArenaEvents {
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onInventoryClose(InventoryCloseEvent event) {
|
||||
Player player = (Player) event.getPlayer();
|
||||
BaseArena baseArena = plugin.getArenaRegistry().getArena(player);
|
||||
if(baseArena != null && baseArena.getArenaInGameState() == BaseArena.ArenaInGameState.BUILD_TIME) {
|
||||
baseArena.addMenuItem(player);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onItemMove(InventoryClickEvent event) {
|
||||
HumanEntity humanEntity = event.getWhoClicked();
|
||||
|
||||
@@ -217,33 +217,6 @@ public class BaseArena extends PluginArena {
|
||||
}
|
||||
}
|
||||
|
||||
HandlerItem optionsMenuItem;
|
||||
|
||||
private HandlerItem getMenuItem() {
|
||||
if(optionsMenuItem == null) {
|
||||
HandlerItem optionsMenu = new HandlerItem(getPlugin().getOptionsRegistry().getMenuItem());
|
||||
optionsMenu.setLeftClick(true);
|
||||
optionsMenu.setRightClick(true);
|
||||
optionsMenu.addInteractHandler(event -> {
|
||||
event.setCancelled(true);
|
||||
getPlugin().getOptionsRegistry().getOptionsGui().open(event.getPlayer());
|
||||
});
|
||||
optionsMenu.addInventoryClickHandler(event -> {
|
||||
getPlugin().getOptionsRegistry().getOptionsGui().open(event.getWhoClicked());
|
||||
});
|
||||
optionsMenuItem = optionsMenu.build();
|
||||
}
|
||||
return optionsMenuItem;
|
||||
}
|
||||
|
||||
public void addMenuItem(Player player) {
|
||||
if(player.getInventory().contains(getMenuItem().getItemStack())) {
|
||||
return;
|
||||
}
|
||||
player.getInventory().setItem(8, getMenuItem().getItemStack());
|
||||
}
|
||||
|
||||
|
||||
public PlotManager getPlotManager() {
|
||||
return plotManager;
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ public class PlotManager {
|
||||
Bukkit.getScheduler().runTaskLater(arena.getPlugin(), () -> {
|
||||
player.setAllowFlight(true);
|
||||
player.setFlying(true);
|
||||
}, 40);
|
||||
}, 2);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -65,7 +65,6 @@ public class InGameState extends PluginInGameState {
|
||||
|
||||
for(Player player : pluginArena.getPlayers()) {
|
||||
player.closeInventory();
|
||||
pluginArena.addMenuItem(player);
|
||||
player.setGameMode(GameMode.CREATIVE);
|
||||
}
|
||||
} else {
|
||||
@@ -103,8 +102,8 @@ public class InGameState extends PluginInGameState {
|
||||
adjustStatistics(pluginArena);
|
||||
|
||||
pluginArena.teleportToWinnerPlot();
|
||||
pluginArena.executeEndRewards();
|
||||
getPlugin().getArenaManager().stopGame(false, arena);
|
||||
pluginArena.executeEndRewards();
|
||||
} else {
|
||||
voteForNextPlot(pluginArena);
|
||||
}
|
||||
|
||||
@@ -72,7 +72,6 @@ public class InGameState extends PluginInGameState {
|
||||
new TitleBuilder("IN_GAME_MESSAGES_PLOT_GTB_THEME_GUESS_TITLE").asKey().arena(pluginArena).sendArena();
|
||||
|
||||
Bukkit.getScheduler().runTaskLater(getPlugin(), () -> pluginArena.getCurrentBuilders().forEach(player -> player.setGameMode(GameMode.CREATIVE)), 40);
|
||||
pluginArena.getCurrentBuilders().forEach(pluginArena::addMenuItem);
|
||||
|
||||
setArenaTimer(getPlugin().getConfig().getInt("Time-Manager." + pluginArena.getArenaType().getPrefix() + ".In-Game"));
|
||||
pluginArena.setArenaInGameState(BaseArena.ArenaInGameState.BUILD_TIME);
|
||||
|
||||
@@ -93,7 +93,7 @@ public class AdditionalValueInitializer {
|
||||
rewardsFactory.registerRewardType("VOTE", new RewardType("voted"));
|
||||
rewardsFactory.registerRewardType("VOTE_ALL", new RewardType("voted-all"));
|
||||
rewardsFactory.registerRewardType("REPORT", new RewardType("report"));
|
||||
rewardsFactory.registerRewardType("PLACE", new RewardType("place"));
|
||||
rewardsFactory.registerRewardType("PLACE", new RewardType("place", RewardType.ExecutorType.NUMBER, false));
|
||||
}
|
||||
|
||||
private void registerSpecialItems() {
|
||||
|
||||
@@ -30,6 +30,7 @@ import plugily.projects.buildbattle.commands.arguments.admin.plot.AddPlotArgumen
|
||||
import plugily.projects.buildbattle.commands.arguments.admin.plot.RemovePlotArgument;
|
||||
import plugily.projects.buildbattle.commands.arguments.admin.plot.SelectPlotArgument;
|
||||
import plugily.projects.buildbattle.commands.arguments.game.GuessArgument;
|
||||
import plugily.projects.buildbattle.commands.arguments.game.MenuArgument;
|
||||
import plugily.projects.minigamesbox.api.arena.IPluginArena;
|
||||
import plugily.projects.minigamesbox.classic.commands.arguments.PluginArgumentsRegistry;
|
||||
|
||||
@@ -50,6 +51,7 @@ public class ArgumentsRegistry extends PluginArgumentsRegistry {
|
||||
new AddPlotArgument(this);
|
||||
new RemovePlotArgument(this);
|
||||
new SelectPlotArgument(this);
|
||||
new MenuArgument(this);
|
||||
new GuessArgument(this);
|
||||
new ThemeArgument(this);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
*
|
||||
* BuildBattle - Ultimate building competition minigame
|
||||
* Copyright (C) 2022 Plugily Projects - maintained by Tigerpanzer_02 and contributors
|
||||
*
|
||||
* 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, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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 plugily.projects.buildbattle.commands.arguments.game;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import plugily.projects.buildbattle.arena.BaseArena;
|
||||
import plugily.projects.buildbattle.commands.arguments.ArgumentsRegistry;
|
||||
import plugily.projects.minigamesbox.api.arena.IArenaState;
|
||||
import plugily.projects.minigamesbox.classic.commands.arguments.data.CommandArgument;
|
||||
|
||||
/**
|
||||
* @author Tigerpanzer_02
|
||||
* <p>
|
||||
* Created at 27.12.2020
|
||||
*/
|
||||
public class MenuArgument {
|
||||
|
||||
public MenuArgument(ArgumentsRegistry registry) {
|
||||
registry.mapArgument("buildbattle", new CommandArgument("menu", "", CommandArgument.ExecutorType.PLAYER) {
|
||||
@Override
|
||||
public void execute(CommandSender sender, String[] args) {
|
||||
Player player = (Player) sender;
|
||||
BaseArena arena = (BaseArena) registry.getPlugin().getArenaRegistry().getArena(player);
|
||||
|
||||
if(arena != null && (arena.getArenaState() == IArenaState.IN_GAME))
|
||||
arena.getPlugin().getOptionsRegistry().getOptionsGui().open(player);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
+2
-1
@@ -67,8 +67,9 @@ public class TimeChangeOption {
|
||||
return;
|
||||
}
|
||||
|
||||
gui.addItem(new ItemBuilder(XMaterial.CLOCK.parseItem()).name(new MessageBuilder("MENU_OPTION_CONTENT_TIME_TYPE_WORLD").asKey().build()).lore(plot.getTime().name()).build());
|
||||
//gui.addItem(new ItemBuilder(XMaterial.CLOCK.parseItem()).name(new MessageBuilder("MENU_OPTION_CONTENT_TIME_TYPE_WORLD").asKey().build()).lore(plot.getTime().name()).build());
|
||||
|
||||
addClockItem(gui, plot, "MENU_OPTION_CONTENT_TIME_TYPE_WORLD");
|
||||
addClockItem(gui, plot, "MENU_OPTION_CONTENT_TIME_TYPE_DAY");
|
||||
addClockItem(gui, plot, "MENU_OPTION_CONTENT_TIME_TYPE_NOON");
|
||||
addClockItem(gui, plot, "MENU_OPTION_CONTENT_TIME_TYPE_SUNSET");
|
||||
|
||||
@@ -37,26 +37,20 @@ public class ChunkManager {
|
||||
|
||||
private static Class<?> packetPlayOutMapChunk, chunkClass;
|
||||
private static Constructor<?> mapChunkConstructor;
|
||||
private static Method chunkHandleMethod;
|
||||
|
||||
static {
|
||||
if(ServerVersion.Version.isCurrentLower(ServerVersion.Version.v1_18_R1)) {
|
||||
if (ServerVersion.Version.isCurrentLower(ServerVersion.Version.v1_18_R1)) {
|
||||
packetPlayOutMapChunk = PacketUtils.classByName("net.minecraft.network.protocol.game", "PacketPlayOutMapChunk");
|
||||
chunkClass = PacketUtils.classByName("net.minecraft.world.level.chunk", "Chunk");
|
||||
|
||||
try {
|
||||
chunkHandleMethod = Chunk.class.getMethod("getHandle");
|
||||
} catch(NoSuchMethodException exc) {
|
||||
}
|
||||
|
||||
if(ServerVersion.Version.isCurrentEqualOrHigher(ServerVersion.Version.v1_17_R1)) {
|
||||
if (ServerVersion.Version.isCurrentEqualOrHigher(ServerVersion.Version.v1_17_R1)) {
|
||||
try {
|
||||
mapChunkConstructor = packetPlayOutMapChunk.getConstructor(PacketUtils.classByName("net.minecraft.world.level.chunk", "LevelChunk"));
|
||||
} catch(NoSuchMethodException e) {
|
||||
} catch (NoSuchMethodException e) {
|
||||
try {
|
||||
mapChunkConstructor = packetPlayOutMapChunk.getConstructor(chunkClass);
|
||||
} catch(NoSuchMethodException ex) {
|
||||
ex.printStackTrace();
|
||||
} catch (NoSuchMethodException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,38 +58,39 @@ public class ChunkManager {
|
||||
}
|
||||
|
||||
public static void sendMapChunk(Player player, Chunk chunk) {
|
||||
if(ServerVersion.Version.isCurrentEqualOrHigher(ServerVersion.Version.v1_18_R1)) {
|
||||
if (ServerVersion.Version.isCurrentEqualOrHigher(ServerVersion.Version.v1_18_R1)) {
|
||||
return; // Should just use World#refreshChunk instead
|
||||
}
|
||||
|
||||
try {
|
||||
if(ServerVersion.Version.isCurrentEqualOrHigher(ServerVersion.Version.v1_17_R1)) {
|
||||
Method chunkHandleMethod = chunk.getClass().getMethod("getHandle");
|
||||
if (ServerVersion.Version.isCurrentEqualOrHigher(ServerVersion.Version.v1_17_R1)) {
|
||||
PacketUtils.sendPacket(player, mapChunkConstructor.newInstance(chunkHandleMethod.invoke(chunk)));
|
||||
return;
|
||||
}
|
||||
|
||||
if(ServerVersion.Version.isCurrentEqualOrHigher(ServerVersion.Version.v1_16_R1)) {
|
||||
if(mapChunkConstructor == null)
|
||||
if (ServerVersion.Version.isCurrentEqualOrHigher(ServerVersion.Version.v1_16_R1)) {
|
||||
if (mapChunkConstructor == null)
|
||||
mapChunkConstructor = packetPlayOutMapChunk.getConstructor(chunkClass, int.class, boolean.class);
|
||||
|
||||
PacketUtils.sendPacket(player, mapChunkConstructor.newInstance(chunkHandleMethod.invoke(chunk), 65535, false));
|
||||
return;
|
||||
}
|
||||
|
||||
if(ServerVersion.Version.isCurrentEqualOrLower(ServerVersion.Version.v1_10_R2)) {
|
||||
if(mapChunkConstructor == null)
|
||||
if (ServerVersion.Version.isCurrentEqualOrLower(ServerVersion.Version.v1_10_R2)) {
|
||||
if (mapChunkConstructor == null)
|
||||
mapChunkConstructor = packetPlayOutMapChunk.getConstructor(chunkClass, boolean.class, int.class);
|
||||
|
||||
PacketUtils.sendPacket(player, mapChunkConstructor.newInstance(chunkHandleMethod.invoke(chunk), true, 65535));
|
||||
return;
|
||||
}
|
||||
|
||||
if(mapChunkConstructor == null)
|
||||
if (mapChunkConstructor == null)
|
||||
mapChunkConstructor = packetPlayOutMapChunk.getConstructor(chunkClass, int.class);
|
||||
|
||||
PacketUtils.sendPacket(player, mapChunkConstructor.newInstance(chunkHandleMethod.invoke(chunk), 65535));
|
||||
} catch(ReflectiveOperationException exception) {
|
||||
exception.printStackTrace();
|
||||
} catch (ReflectiveOperationException exception) {
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -393,7 +393,7 @@ Menu:
|
||||
Noon: "Noon (6000 ticks)"
|
||||
Sunset: "Sunset (12000 ticks)"
|
||||
Night: "Night (13000 ticks)"
|
||||
MidNight: "MidNight (18000 ticks)"
|
||||
MidNight: "Midnight (18000 ticks)"
|
||||
Sunrise: "Sunrise (23000 ticks)"
|
||||
Changed: "%plugin_prefix% Time has been changed to %value%"
|
||||
Biome:
|
||||
|
||||
@@ -8,11 +8,11 @@ api-version: 1.13
|
||||
commands:
|
||||
buildbattle:
|
||||
description: BuildBattle Commands
|
||||
usage: "\u00A76Correct usage: /bb [option]"
|
||||
usage: "§6Correct usage: /bb [option]"
|
||||
aliases: [ bb, buildb ]
|
||||
buildbattleadmin:
|
||||
description: BuildBattle Admin Commands
|
||||
usage: "\u00A76Correct usage: /bba [option]"
|
||||
usage: "§6Correct usage: /bba [option]"
|
||||
aliases: [ bba, buildbadmin ]
|
||||
|
||||
permissions:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# BuildBattle rewards configuration
|
||||
#
|
||||
# Placeholders list:
|
||||
# %PLAYER% - Current player name
|
||||
# %player% - Current player name
|
||||
# %MAPNAME% - Name of map
|
||||
# %ARENA-ID% - BaseArena Identifier
|
||||
# %PLACE% - Player Place (Working on category place)
|
||||
@@ -17,11 +17,11 @@
|
||||
# - chance(1):p:say I was very lucky! # Player has 1% chance to say "I was very lucky!"
|
||||
# - p:chance(99):spawn # Player has 99% chance to teleport to spawn
|
||||
# ^ YOU CAN EVEN SWAP CHANCE WITH PLAYER!
|
||||
# - chance(50):eco give %PLAYER% 10 # Console has 10% chance to give player 10$
|
||||
# - chance(50):eco give %player% 10 # Console has 10% chance to give player 10$
|
||||
#
|
||||
# You can unlock full potential of rewards using our script engine! (since 4.0.0)
|
||||
# Just add example reward:
|
||||
# - script:player.sendMessage("oh, hi %PLAYER%"); # It will send "oh, hi <player name>" to player! 100% plain java!
|
||||
# - script:player.sendMessage("oh, hi %player%"); # It will send "oh, hi <player name>" to player! 100% plain java!
|
||||
# - script:server.broadcastMessage("hello everyone"); # Broadcasts "hello everyone" to whole server
|
||||
# - script:player.getInventory().addItem(new org.bukkit.inventory.ItemStack(org.bukkit.Material.DIRT));
|
||||
# ^ Gives player dirt item (you must always use direct package names for not provided objects)
|
||||
@@ -50,40 +50,40 @@ rewards:
|
||||
# Commands performed when all themes are guessed in GTB
|
||||
# Delete everything if you don't want to use this section
|
||||
guess:
|
||||
- eco give %PLAYER% 2
|
||||
- eco give %player% 2
|
||||
guess-all:
|
||||
- eco give %PLAYER% 2
|
||||
- eco give %player% 2
|
||||
# Commands executed when someone vote
|
||||
vote:
|
||||
- eco give %PLAYER% 2
|
||||
- chance(10):eco give %PLAYER% 8
|
||||
- eco give %player% 2
|
||||
- chance(10):eco give %player% 8
|
||||
# Commands executed when all voted for a plot
|
||||
vote-all:
|
||||
- eco give %PLAYER% 2
|
||||
- chance(10):eco give %PLAYER% 8
|
||||
- eco give %player% 2
|
||||
- chance(10):eco give %player% 8
|
||||
# Commands executed when someone report a building
|
||||
report:
|
||||
- eco give %PLAYER% 2
|
||||
- eco give %player% 2
|
||||
- script:player.sendMessage("You reported the building");
|
||||
# Commands executed on place X
|
||||
place:
|
||||
1:
|
||||
- eco give %PLAYER% 10
|
||||
- eco give %player% 10
|
||||
2:
|
||||
- eco give %PLAYER% 9
|
||||
- eco give %player% 9
|
||||
3:
|
||||
- eco give %PLAYER% 8
|
||||
- eco give %player% 8
|
||||
4:
|
||||
- eco give %PLAYER% 7
|
||||
- eco give %player% 7
|
||||
5:
|
||||
- eco give %PLAYER% 6
|
||||
- eco give %player% 6
|
||||
6:
|
||||
- eco give %PLAYER% 5
|
||||
- eco give %player% 5
|
||||
7:
|
||||
- eco give %PLAYER% 4
|
||||
- eco give %player% 4
|
||||
8:
|
||||
- eco give %PLAYER% 3
|
||||
- eco give %player% 3
|
||||
9:
|
||||
- eco give %PLAYER% 2
|
||||
- eco give %player% 2
|
||||
10:
|
||||
- eco give %PLAYER% 1
|
||||
- eco give %player% 1
|
||||
@@ -121,7 +121,7 @@ Back-To-Hub:
|
||||
Options-Menu:
|
||||
permission: ""
|
||||
execute:
|
||||
- ""
|
||||
- "p:bb menu"
|
||||
displayname: "&a&lOptions menu &7(Right Click)"
|
||||
lore:
|
||||
- "&7Right-click to open options menu"
|
||||
|
||||
Reference in new issue
Block a user