Skip to content

Commit

Permalink
Implemented entity Sniffer (#1259)
Browse files Browse the repository at this point in the history
* Implemented Sniffer

* Registered Sniffer Entity

* Applied code review suggestions
  • Loading branch information
4everTheOne authored Jan 19, 2025
1 parent a6b20cc commit 2bed639
Show file tree
Hide file tree
Showing 4 changed files with 294 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
import org.bukkit.entity.SkeletonHorse;
import org.bukkit.entity.Slime;
import org.bukkit.entity.SmallFireball;
import org.bukkit.entity.Sniffer;
import org.bukkit.entity.Snowball;
import org.bukkit.entity.Snowman;
import org.bukkit.entity.SpectralArrow;
Expand Down Expand Up @@ -297,6 +298,7 @@ public static Builder withDefaults()
.register(SkeletonHorse.class, SkeletonHorseMock.class, SkeletonHorseMock::new)
.register(Slime.class, SlimeMock.class, SlimeMock::new)
.register(SmallFireball.class, SmallFireballMock.class, SmallFireballMock::new)
.register(Sniffer.class, SnifferMock.class, SnifferMock::new)
.register(Snowball.class, SnowballMock.class, SnowballMock::new)
.register(Snowman.class, SnowmanMock.class, SnowmanMock::new)
.register(SpawnerMinecart.class, SpawnerMinecartMock.class, SpawnerMinecartMock::new)
Expand Down
136 changes: 136 additions & 0 deletions src/main/java/org/mockbukkit/mockbukkit/entity/SnifferMock.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package org.mockbukkit.mockbukkit.entity;

import com.google.common.base.Preconditions;
import net.kyori.adventure.sound.Sound;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Sniffer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.mockbukkit.mockbukkit.ServerMock;
import org.mockbukkit.mockbukkit.exception.UnimplementedOperationException;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.UUID;


/**
* Mock implementation of an {@link Sniffer}.
*
* @see AnimalsMock
*/
public class SnifferMock extends AnimalsMock implements Sniffer
{
private final List<Location> exploredLocations = new ArrayList<>();

private State state = State.IDLING;

/**
* Constructs a new {@link Sniffer} on the provided {@link ServerMock} with a specified {@link UUID}.
*
* @param server The server to create the entity on.
* @param uuid The UUID of the entity.
*/
public SnifferMock(@NotNull ServerMock server, @NotNull UUID uuid)
{
super(server, uuid);
}

@Override
public @NotNull Collection<Location> getExploredLocations()
{
return Collections.unmodifiableList(exploredLocations);
}

@Override
public void removeExploredLocation(@NotNull Location location)
{
Preconditions.checkArgument(location != null, "location cannot be null");

Location blockPosition = location.toBlockLocation();
World world = blockPosition.getWorld() == null ? getWorld() : blockPosition.getWorld();
blockPosition.setWorld(world);
exploredLocations.remove(blockPosition);
}

@Override
public void addExploredLocation(@NotNull Location location)
{
Preconditions.checkArgument(location != null, "location cannot be null");
if (location.getWorld() != this.getWorld())
{
return;
}

exploredLocations.add(location.toBlockLocation());
}

@Override
public @NotNull State getState()
{
return this.state;
}

@Override
public void setState(@NotNull State state)
{
Preconditions.checkArgument(state != null, "state cannot be null");

this.state = state;

switch (state)
{
case FEELING_HAPPY -> playSound(Sound.sound(
org.bukkit.Sound.ENTITY_SNIFFER_HAPPY,
Sound.Source.AMBIENT,
1.0f,
1.0f
));
case SCENTING -> playSound(Sound.sound(
org.bukkit.Sound.ENTITY_SNIFFER_SCENTING,
Sound.Source.AMBIENT,
1.0f,
isAdult() ? 1.0f : 1.3F
));
case SNIFFING -> playSound(Sound.sound(
org.bukkit.Sound.ENTITY_SNIFFER_SNIFFING,
Sound.Source.AMBIENT,
1.0f,
1.0f
));
case RISING -> playSound(Sound.sound(
org.bukkit.Sound.ENTITY_SNIFFER_DIGGING_STOP,
Sound.Source.AMBIENT,
1.0f,
1.0f
));
default ->
{
// No sound is emitted
}
}
}

@Override
public @Nullable Location findPossibleDigLocation()
{
throw new UnimplementedOperationException("Method findPossibleDigLocation is not implemented.");
}

@Override
public boolean canDig()
{
throw new UnimplementedOperationException("Method canDig is not implemented.");
}

@Override
public @NotNull EntityType getType()
{
return EntityType.SNIFFER;
}

}
153 changes: 153 additions & 0 deletions src/test/java/org/mockbukkit/mockbukkit/entity/SnifferMockTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package org.mockbukkit.mockbukkit.entity;

import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Sniffer;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.mockbukkit.mockbukkit.MockBukkitExtension;
import org.mockbukkit.mockbukkit.MockBukkitInject;
import org.mockbukkit.mockbukkit.ServerMock;

import java.util.Collection;
import java.util.List;
import java.util.UUID;

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

@ExtendWith(MockBukkitExtension.class)
class SnifferMockTest
{
@MockBukkitInject
private ServerMock server;
private SnifferMock sniffer;

@BeforeEach
void setUp()
{
sniffer = new SnifferMock(server, UUID.randomUUID());
}

@Nested
class AddExploredLocations
{

@Test
void defaultExploredLocationsIsEmpty()
{
@NotNull Collection<Location> actual = sniffer.getExploredLocations();
assertNotNull(actual);
assertTrue(actual.isEmpty());
}

@Test
void givenNullLocation()
{
IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> sniffer.addExploredLocation(null));
assertEquals("location cannot be null", e.getMessage());
}

@Test
void givenDifferentWorlds()
{
World worldA = server.addSimpleWorld("worldA");
sniffer.setLocation(new Location(worldA, 0, 0, 0));

World worldB = server.addSimpleWorld("worldB");
sniffer.addExploredLocation(new Location(worldB, 0.5, 0.5, 0.5));
assertTrue(sniffer.getExploredLocations().isEmpty());
}

@Test
void givenSameWorld()
{
World worldA = server.addSimpleWorld("worldA");
Location spawnLocation = new Location(worldA, 0, 0, 0);
sniffer.setLocation(spawnLocation);

Location location = new Location(worldA, 0.5, -1.5, 0.5);
sniffer.addExploredLocation(location);

assertEquals(List.of(location.toBlockLocation()), sniffer.getExploredLocations());
}

}

@Nested
class RemoveExploredLocation
{

@Test
void givenNullLocation()
{
IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> sniffer.removeExploredLocation(null));
assertEquals("location cannot be null", e.getMessage());
}

@Test
void givenNullWorldShouldDefaultForCurrentWorld()
{
World worldA = server.addSimpleWorld("worldA");
Location spawnLocation = new Location(worldA, 0, 0, 0);
sniffer.setLocation(spawnLocation);

Location locationA = new Location(worldA, 0.5, -1.5, 0.5);
sniffer.addExploredLocation(locationA);

Location locationB = new Location(null, 0.5, -1.5, 0.5);
sniffer.removeExploredLocation(locationB);
assertTrue(sniffer.getExploredLocations().isEmpty());
}

@Test
void givenSameWorld()
{
World worldA = server.addSimpleWorld("worldA");
Location spawnLocation = new Location(worldA, 0, 0, 0);
sniffer.setLocation(spawnLocation);

Location location = new Location(worldA, 0.5, -1.5, 0.5);
sniffer.addExploredLocation(location);
sniffer.removeExploredLocation(location);
assertTrue(sniffer.getExploredLocations().isEmpty());
}

}

@Nested
class SetState
{

@Test
void givenDefaultState()
{
assertEquals(Sniffer.State.IDLING, sniffer.getState());
}

@ParameterizedTest
@EnumSource(Sniffer.State.class)
void givenPossibleStates(Sniffer.State state)
{
assertDoesNotThrow(() -> sniffer.setState(state));
assertEquals(state, sniffer.getState());
}

}

@Test
void getType()
{
assertEquals(EntityType.SNIFFER, sniffer.getType());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@
import org.mockbukkit.mockbukkit.entity.SkeletonMock;
import org.mockbukkit.mockbukkit.entity.SlimeMock;
import org.mockbukkit.mockbukkit.entity.SmallFireballMock;
import org.mockbukkit.mockbukkit.entity.SnifferMock;
import org.mockbukkit.mockbukkit.entity.SnowballMock;
import org.mockbukkit.mockbukkit.entity.SnowmanMock;
import org.mockbukkit.mockbukkit.entity.SpawnerMinecartMock;
Expand Down Expand Up @@ -1448,7 +1449,8 @@ public static Stream<Arguments> getSpawnableEntities()
Arguments.of(EntityType.BREEZE_WIND_CHARGE, BreezeWindChargeMock.class),
Arguments.of(EntityType.WIND_CHARGE, WindChargeMock.class),
Arguments.of(EntityType.WITHER, WitherMock.class),
Arguments.of(EntityType.ENDER_DRAGON, EnderDragonMock.class)
Arguments.of(EntityType.ENDER_DRAGON, EnderDragonMock.class),
Arguments.of(EntityType.SNIFFER, SnifferMock.class)
);
}

Expand Down

0 comments on commit 2bed639

Please sign in to comment.