develop-turtleGame #6
@@ -21,6 +21,7 @@ import eu.mhsl.minenet.minigames.instance.game.stateless.types.sumo.SumoFactory;
|
||||
import eu.mhsl.minenet.minigames.instance.game.stateless.types.tntrun.TntRunFactory;
|
||||
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.TowerdefenseFactory;
|
||||
import eu.mhsl.minenet.minigames.instance.game.stateless.types.trafficlightrace.TrafficLightRaceFactory;
|
||||
import eu.mhsl.minenet.minigames.instance.game.stateless.types.turtleGame.TurtleGameFactory;
|
||||
|
||||
public enum GameList {
|
||||
DEATHCUBE(new DeathcubeFactory(), GameType.JUMPNRUN),
|
||||
@@ -35,6 +36,7 @@ public enum GameList {
|
||||
TNTRUN(new TntRunFactory(), GameType.OTHER),
|
||||
ANVILRUN(new AnvilRunFactory(), GameType.PVE),
|
||||
ACIDRAIN(new AcidRainFactory(), GameType.PVE),
|
||||
TURTLEGAME(new TurtleGameFactory(), GameType.PVE),
|
||||
ELYTRARACE(new ElytraRaceFactory(), GameType.PVP),
|
||||
SPLEEF(new SpleefFactory(), GameType.PVP),
|
||||
JUMPDIVE(new JumpDiveFactory(), GameType.JUMPNRUN),
|
||||
|
@@ -0,0 +1,254 @@
|
||||
package eu.mhsl.minenet.minigames.instance.game.stateless.types.turtleGame;
|
||||
|
||||
import eu.mhsl.minenet.minigames.instance.Dimension;
|
||||
import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame;
|
||||
import eu.mhsl.minenet.minigames.instance.game.stateless.types.turtleGame.gameObjects.Turtle;
|
||||
import eu.mhsl.minenet.minigames.score.PointsWinScore;
|
||||
import net.kyori.adventure.sound.Sound;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.coordinate.Pos;
|
||||
import net.minestom.server.entity.*;
|
||||
import net.minestom.server.entity.metadata.other.FallingBlockMeta;
|
||||
import net.minestom.server.event.player.PlayerStartSneakingEvent;
|
||||
import net.minestom.server.event.player.PlayerStopSneakingEvent;
|
||||
import net.minestom.server.event.player.PlayerTickEvent;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.item.Material;
|
||||
import net.minestom.server.network.packet.server.play.ParticlePacket;
|
||||
import net.minestom.server.particle.Particle;
|
||||
import net.minestom.server.sound.SoundEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
class TurtleGame extends StatelessGame {
|
||||
private final int radius;
|
||||
private final Map<Player, Turtle> turtlePlayerMap = new WeakHashMap<>();
|
||||
private final ArrayList<Entity> snacks = new ArrayList<>();
|
||||
private final ArrayList<Entity> bombs = new ArrayList<>();
|
||||
private final Block snackBlock = Block.SUNFLOWER.withProperty("half", "upper");
|
||||
private final double startSpeed;
|
||||
|
||||
public TurtleGame(int radius, int startSpeed) {
|
||||
super(Dimension.OVERWORLD.key, "Turtle Game", new PointsWinScore());
|
||||
this.radius = radius;
|
||||
this.startSpeed = startSpeed;
|
||||
|
||||
this.eventNode()
|
||||
.addListener(PlayerTickEvent.class, this::onPlayerTick)
|
||||
.addListener(PlayerStartSneakingEvent.class, this::onPlayerStartSneak)
|
||||
.addListener(PlayerStopSneakingEvent.class, this::onPlayerStopSneak);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLoad(@NotNull CompletableFuture<Void> callback) {
|
||||
for(int x = -this.radius - 1; x <= this.radius + 1; x++) {
|
||||
for(int z = -this.radius - 1; z <= this.radius + 1; z++) {
|
||||
double distance = new Pos(x, 0, z).distance(new Pos(0, 0, 0));
|
||||
if(distance <= this.radius) {
|
||||
Pupsi marked this conversation as resolved
|
||||
boolean isEdge = this.radius - 1 < distance;
|
||||
this.setBlock(x, 0, z, Block.SAND);
|
||||
this.setBlock(x, 1, z, isEdge ? Block.STONE : Block.AIR);
|
||||
this.setBlock(x, 2, z, isEdge ? Block.STONE_SLAB : Block.AIR);
|
||||
} else {
|
||||
this.setBlock(x, 0, z, Block.AIR);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onPlayerStartSneak(@NotNull PlayerStartSneakingEvent event) {
|
||||
Player p = event.getPlayer();
|
||||
this.turtlePlayerMap.get(p).boostSpeed();
|
||||
}
|
||||
|
||||
private void onPlayerStopSneak(@NotNull PlayerStopSneakingEvent event) {
|
||||
Player p = event.getPlayer();
|
||||
this.turtlePlayerMap.get(p).cancelBoost();
|
||||
}
|
||||
|
||||
protected void onPlayerTick(@NotNull PlayerTickEvent event) {
|
||||
Player p = event.getPlayer();
|
||||
if(p.getGameMode() == GameMode.SPECTATOR) return;
|
||||
Turtle turtle = this.turtlePlayerMap.get(p);
|
||||
turtle.adaptView();
|
||||
if(this.isRunning()) {
|
||||
turtle.move();
|
||||
this.snacks.stream()
|
||||
.filter(turtle::checkCollisionWithEntity)
|
||||
.toList()
|
||||
.forEach(snack -> {
|
||||
this.eat(p, snack);
|
||||
this.generateNewSnack();
|
||||
if(this.turtlePlayerMap.get(p).getScore() % 5 == 0) {
|
||||
this.generateNewBomb();
|
||||
this.addSpeed(0.4, p);
|
||||
}
|
||||
});
|
||||
this.bombs.stream()
|
||||
.filter(turtle::checkCollisionWithEntity)
|
||||
.toList()
|
||||
.forEach(bomb -> {
|
||||
this.explode(p, bomb);
|
||||
this.generateNewBombs(2);
|
||||
this.addGlobalSpeed(0.3);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected void addSpeed(double amount, Player p) {
|
||||
this.turtlePlayerMap.get(p).addSpeed(amount);
|
||||
}
|
||||
|
||||
protected void addGlobalSpeed(double amount) {
|
||||
this.turtlePlayerMap.values().forEach(turtle -> turtle.addSpeed(amount));
|
||||
}
|
||||
|
||||
protected void eat(Player p, Entity snack) {
|
||||
p.playSound(Sound.sound(SoundEvent.ENTITY_GENERIC_EAT, Sound.Source.MASTER, 1f, 1f), snack.getPosition());
|
||||
Material snackMaterial = Objects.requireNonNull(this.snackBlock.registry().material());
|
||||
p.sendPacket(new ParticlePacket(Particle.ITEM.withItem(ItemStack.of(snackMaterial)), p.getPosition(), new Pos(0.5, 0.5, 0.5), 0, 8));
|
||||
this.snacks.remove(snack);
|
||||
snack.remove();
|
||||
this.turtlePlayerMap.get(p).increaseScore();
|
||||
this.turtlePlayerMap.get(p).increaseBoostChargeLevel(0.04f);
|
||||
Pupsi marked this conversation as resolved
Outdated
MineTec
commented
ändern zu Objects.requireNotNull() und den fallback zu Material.DIRT weg ändern zu Objects.requireNotNull() und den fallback zu Material.DIRT weg
|
||||
}
|
||||
|
||||
protected void explode(Player p, Entity bomb) {
|
||||
this.letPlayerLoose(p);
|
||||
p.playSound(Sound.sound(SoundEvent.ENTITY_GENERIC_EXPLODE, Sound.Source.MASTER, 2f, 1f), bomb.getPosition());
|
||||
p.playSound(Sound.sound(SoundEvent.ENTITY_GENERIC_EXPLODE, Sound.Source.MASTER, 2f, 1f), bomb.getPosition());
|
||||
p.sendPacket(new ParticlePacket(Particle.EXPLOSION, p.getPosition(), new Pos(0.5, 0.5, 0.5), 0, 8));
|
||||
if(this.getLeftPlayers().size() == 1) this.setTimeLimit(10);
|
||||
this.bombs.remove(bomb);
|
||||
bomb.remove();
|
||||
}
|
||||
|
||||
protected void letPlayerLoose(Player p) {
|
||||
p.setGameMode(GameMode.SPECTATOR);
|
||||
p.setFlying(true);
|
||||
this.turtlePlayerMap.get(p).destroy();
|
||||
this.getScore().insertResult(p, this.turtlePlayerMap.get(p).getScore());
|
||||
}
|
||||
|
||||
protected List<Player> getLeftPlayers() {
|
||||
return this.turtlePlayerMap.keySet().stream()
|
||||
.filter(player -> !this.getScore().hasResult(player))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onPlayerJoin(Player p) {
|
||||
this.turtlePlayerMap.putIfAbsent(p, new Turtle(p, this.startSpeed));
|
||||
p.setLevel(this.turtlePlayerMap.get(p).getScore());
|
||||
|
||||
Turtle turtle = this.turtlePlayerMap.get(p);
|
||||
MinecraftServer.getSchedulerManager().scheduleNextTick(turtle::spawnTurtle);
|
||||
return super.onPlayerJoin(p);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPlayerLeave(Player p) {
|
||||
Turtle turtle = this.turtlePlayerMap.get(p);
|
||||
turtle.remove();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pos getSpawn() {
|
||||
double theta = this.rnd.nextDouble() * 2 * Math.PI;
|
||||
|
||||
double spawnRadius = this.radius - 4;
|
||||
double x = spawnRadius * Math.cos(theta);
|
||||
double z = spawnRadius * Math.sin(theta);
|
||||
|
||||
return new Pos(x, 1, z).withLookAt(new Pos(0, 0, 0));
|
||||
}
|
||||
|
||||
private void generateNewSnacks(int count) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
this.generateNewSnack();
|
||||
}
|
||||
}
|
||||
|
||||
private void generateNewSnack() {
|
||||
Entity snack = new Entity(EntityType.FALLING_BLOCK);
|
||||
FallingBlockMeta meta = (FallingBlockMeta) snack.getEntityMeta();
|
||||
meta.setBlock(this.snackBlock);
|
||||
meta.setCustomName(Component.text("Snack"));
|
||||
meta.setCustomNameVisible(true);
|
||||
Pos spawnPosition = this.newSpawnPosition(snack);
|
||||
if(spawnPosition == null) {
|
||||
snack.remove();
|
||||
Pupsi marked this conversation as resolved
Outdated
MineTec
commented
property doppelt sich? property doppelt sich?
|
||||
return;
|
||||
Pupsi marked this conversation as resolved
Outdated
MineTec
commented
default color ist nicht weiß? default color ist nicht weiß?
|
||||
}
|
||||
snack.setInstance(this, spawnPosition);
|
||||
this.snacks.add(snack);
|
||||
}
|
||||
|
||||
private void generateNewBombs(int count) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
this.generateNewBomb();
|
||||
Pupsi marked this conversation as resolved
Outdated
MineTec
commented
statt teleport direkt bei setInstance als zweiter Parameter die Position übergeben statt teleport direkt bei setInstance als zweiter Parameter die Position übergeben
|
||||
}
|
||||
}
|
||||
|
||||
private void generateNewBomb() {
|
||||
Entity bomb = new Entity(EntityType.FALLING_BLOCK);
|
||||
FallingBlockMeta meta = (FallingBlockMeta) bomb.getEntityMeta();
|
||||
meta.setBlock(Block.TNT);
|
||||
meta.setCustomName(Component.text("Bomb").color(NamedTextColor.RED).decorate(TextDecoration.BOLD));
|
||||
meta.setCustomNameVisible(true);
|
||||
Pos spawnPosition = this.newSpawnPosition(bomb, false);
|
||||
if(spawnPosition == null) {
|
||||
bomb.remove();
|
||||
return;
|
||||
}
|
||||
bomb.setInstance(this, spawnPosition);
|
||||
this.bombs.add(bomb);
|
||||
}
|
||||
|
||||
private @Nullable Pos newSpawnPosition(Entity entity) {
|
||||
return this.newSpawnPosition(entity, true);
|
||||
}
|
||||
|
||||
Pupsi marked this conversation as resolved
Outdated
MineTec
commented
nochmal nochmal
|
||||
private @Nullable Pos newSpawnPosition(Entity entity, boolean nearPlayers) {
|
||||
Pos spawnPosition;
|
||||
int counter = 0;
|
||||
boolean isInRadius, collides;
|
||||
Pupsi marked this conversation as resolved
MineTec
commented
@Nullable gehört vor die Rückgabe parameter @Nullable gehört vor die Rückgabe parameter `private @Nullable Pos newSpawnPosition(`
|
||||
do {
|
||||
if(counter > 200) return null;
|
||||
int x = this.rnd.nextInt(-this.radius+2, this.radius-2);
|
||||
int z = this.rnd.nextInt(-this.radius+2, this.radius-2);
|
||||
spawnPosition = new Pos(x, 1, z).add(0.5, 0, 0.5);
|
||||
Pupsi marked this conversation as resolved
Outdated
MineTec
commented
nochmal nochmal
|
||||
Pos checkPosition = spawnPosition;
|
||||
isInRadius = checkPosition.distance(0, 1, 0) < this.radius-2;
|
||||
collides = this.getEntities().stream()
|
||||
.filter(e -> !e.equals(entity))
|
||||
.anyMatch(e -> entity.getBoundingBox().intersectBox(e.getPosition().sub(checkPosition), e.getBoundingBox()));
|
||||
if(!collides && !nearPlayers) collides = this.turtlePlayerMap.values().stream()
|
||||
Pupsi marked this conversation as resolved
Outdated
MineTec
commented
mMn. curly braces weg mMn. curly braces weg
|
||||
.filter(turtle -> !turtle.equals(entity))
|
||||
.anyMatch(turtle -> entity.getBoundingBox()
|
||||
.growSymmetrically(turtle.getBombBorder(), 1, turtle.getBombBorder())
|
||||
.intersectBox(turtle.getPosition().sub(checkPosition), turtle.getBoundingBox()));
|
||||
counter++;
|
||||
} while (!isInRadius || collides);
|
||||
return spawnPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
this.generateNewSnacks(this.turtlePlayerMap.size() + 1);
|
||||
this.generateNewBombs((int) Math.ceil(this.snacks.size() * 0.5));
|
||||
this.turtlePlayerMap.values().forEach(Turtle::startBoostRefill);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
this.getLeftPlayers().forEach(this::letPlayerLoose);
|
||||
}
|
||||
}
|
@@ -0,0 +1,42 @@
|
||||
package eu.mhsl.minenet.minigames.instance.game.stateless.types.turtleGame;
|
||||
|
||||
import eu.mhsl.minenet.minigames.instance.game.Game;
|
||||
import eu.mhsl.minenet.minigames.instance.game.stateless.config.ConfigManager;
|
||||
import eu.mhsl.minenet.minigames.instance.game.stateless.config.GameFactory;
|
||||
import eu.mhsl.minenet.minigames.instance.game.stateless.config.Option;
|
||||
import eu.mhsl.minenet.minigames.instance.game.stateless.config.common.NumericOption;
|
||||
import eu.mhsl.minenet.minigames.instance.room.Room;
|
||||
import eu.mhsl.minenet.minigames.message.component.TranslatedComponent;
|
||||
import net.minestom.server.item.Material;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class TurtleGameFactory implements GameFactory {
|
||||
|
||||
@Override
|
||||
public TranslatedComponent name() {
|
||||
return TranslatedComponent.byId("game_TurtleGame#name");
|
||||
}
|
||||
|
||||
@Override
|
||||
public TranslatedComponent description() {
|
||||
return TranslatedComponent.byId("game_TurtleGame#description");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigManager configuration() {
|
||||
return new ConfigManager()
|
||||
.addOption(new NumericOption("radius", Material.HEART_OF_THE_SEA, TranslatedComponent.byId("optionCommon#radius"), 10, 20, 30, 40))
|
||||
.addOption(new NumericOption("startSpeed", Material.LEATHER_BOOTS, TranslatedComponent.byId("game_TurtleGame#startSpeed"), 2, 3, 4, 6, 8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Game manufacture(Room parent, Map<String, Option<?>> configuration) throws Exception {
|
||||
return new TurtleGame(configuration.get("radius").getAsInt(), configuration.get("startSpeed").getAsInt()).setParent(parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Material symbol() {
|
||||
return Material.TURTLE_EGG;
|
||||
}
|
||||
}
|
@@ -0,0 +1,110 @@
|
||||
package eu.mhsl.minenet.minigames.instance.game.stateless.types.turtleGame.gameObjects;
|
||||
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.coordinate.Vec;
|
||||
import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.entity.EntityCreature;
|
||||
import net.minestom.server.entity.EntityType;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.entity.attribute.Attribute;
|
||||
import net.minestom.server.timer.Task;
|
||||
import net.minestom.server.timer.TaskSchedule;
|
||||
|
||||
public class Turtle extends EntityCreature {
|
||||
private final Player player;
|
||||
private int score = 0;
|
||||
private double speed;
|
||||
private double boostSpeedMultiplier = 1;
|
||||
private float boostChargeLevel = 0f;
|
||||
private Task boostTask;
|
||||
private Task boostRefillTask;
|
||||
|
||||
public Turtle(Player player, double speed) {
|
||||
super(EntityType.TURTLE);
|
||||
this.player = player;
|
||||
this.speed = speed;
|
||||
|
||||
this.getAttribute(Attribute.MOVEMENT_SPEED).setBaseValue(0.15);
|
||||
this.getAttribute(Attribute.MAX_HEALTH).setBaseValue(1);
|
||||
this.player.setExp(this.boostChargeLevel);
|
||||
}
|
||||
|
||||
public void spawnTurtle() {
|
||||
this.setInstance(this.player.getInstance(), this.player.getPosition());
|
||||
this.addPassenger(this.player);
|
||||
Pupsi marked this conversation as resolved
MineTec
commented
statt teleport bei setInstance übergeben statt teleport bei setInstance übergeben
|
||||
}
|
||||
|
||||
public void startBoostRefill() {
|
||||
this.boostRefillTask = MinecraftServer.getSchedulerManager().scheduleTask(() -> {
|
||||
if(this.boostChargeLevel >= 1f) return;
|
||||
this.increaseBoostChargeLevel(0.02f);
|
||||
}, TaskSchedule.seconds(1), TaskSchedule.seconds(1));
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
this.removePassenger(this.player);
|
||||
this.remove();
|
||||
if(this.boostRefillTask != null && this.boostRefillTask.isAlive()) this.boostRefillTask.cancel();
|
||||
if(this.boostTask != null && this.boostTask.isAlive()) this.boostTask.cancel();
|
||||
Pupsi marked this conversation as resolved
Outdated
MineTec
commented
kill nach remove? macht das überhaupt irgendwas? kill nach remove? macht das überhaupt irgendwas?
Pupsi
commented
nein nein
|
||||
}
|
||||
|
||||
public void adaptView() {
|
||||
if(this.getInstance() == null) return;
|
||||
this.teleport(this.getPosition().withView(this.player.getPosition().withPitch(this.getPosition().pitch())));
|
||||
Vec lookingVector = this.player.getPosition().direction().withY(0).mul(100);
|
||||
this.lookAt(this.getPosition().add(lookingVector.asPosition()));
|
||||
}
|
||||
|
||||
public void move() {
|
||||
this.adaptView();
|
||||
Vec direction = this.player.getPosition().direction();
|
||||
Vec movementVector = direction.withY(0).normalize().mul(this.speed * this.boostSpeedMultiplier);
|
||||
this.setVelocity(movementVector);
|
||||
}
|
||||
|
||||
public boolean checkCollisionWithEntity(Entity other) {
|
||||
return this.getBoundingBox().intersectBox(other.getPosition().sub(this.getPosition()), other.getBoundingBox());
|
||||
}
|
||||
|
||||
public void boostSpeed() {
|
||||
if(this.boostChargeLevel <= 0f) return;
|
||||
this.boostSpeedMultiplier = 3.5;
|
||||
this.boostTask = MinecraftServer.getSchedulerManager().scheduleTask(() -> {
|
||||
if(this.boostChargeLevel <= 0f) {
|
||||
this.cancelBoost();
|
||||
return;
|
||||
}
|
||||
this.boostChargeLevel = Math.max(0f, this.boostChargeLevel - 0.025f);
|
||||
this.player.setExp(this.boostChargeLevel);
|
||||
}, TaskSchedule.millis(30), TaskSchedule.millis(30));
|
||||
Pupsi marked this conversation as resolved
MineTec
commented
raus raus
|
||||
}
|
||||
|
||||
public void cancelBoost() {
|
||||
if(this.boostTask == null || !this.boostTask.isAlive()) return;
|
||||
this.boostTask.cancel();
|
||||
this.boostSpeedMultiplier = 1;
|
||||
}
|
||||
|
||||
public void increaseBoostChargeLevel(float amount) {
|
||||
this.boostChargeLevel = Math.min(1f, this.boostChargeLevel + amount);
|
||||
this.player.setExp(this.boostChargeLevel);
|
||||
}
|
||||
|
||||
public void addSpeed(double amount) {
|
||||
this.speed += amount;
|
||||
}
|
||||
|
||||
public int getScore() {
|
||||
return this.score;
|
||||
}
|
||||
|
||||
public double getBombBorder() {
|
||||
// 1 bei speed 2; 2 bei speed 4; 4 bei speed 8
|
||||
return Math.clamp((this.speed * this.boostSpeedMultiplier) / 2, 1.5, 4);
|
||||
}
|
||||
|
||||
public void increaseScore() {
|
||||
this.score += 1;
|
||||
this.player.setLevel(this.score);
|
||||
}
|
||||
}
|
@@ -152,3 +152,8 @@ ns:game_SpaceSnake#;;
|
||||
name;Space Snake;Weltraum-Snake
|
||||
description;Collect diamonds while extending your snake bridge through space and fight other players. The player with the longest bridge wins!;Sammle Diamanten, während du deine Schlangenbrücke durchs All erweiterst und gegen andere Spieler kämpfst. Der Spieler mit den der Längsten Brücke gewinnt!
|
||||
powerUpCount;Number of diamonds in the arena;Anzahl der Diamanten in der Arena
|
||||
;;
|
||||
ns:game_TurtleGame#;;
|
||||
name;Turtle Game;Turtle Game
|
||||
description;Eat snacks and dodge bombs to get the highest score!;Esse Snacks und weiche Bomben aus, um den höchsten Score zu erreichen!
|
||||
startSpeed;Start Speed;Startgeschwindigkeit
|
||||
|
Can't render this file because it has a wrong number of fields in line 114.
|
Reference in New Issue
Block a user
eigne methode ist hier irgendwie unnötig...