Archived
0

Merge branch 'world' into proto_1.12.2

This commit is contained in:
2018-10-10 22:26:19 +03:00
24 changed files with 318 additions and 139 deletions

View File

@@ -1,12 +0,0 @@
package mc.core.exception;
public abstract class McCoreUncheckedException extends RuntimeException {
public McCoreUncheckedException() {
super();
}
public McCoreUncheckedException(String msg) {
super(msg);
}
}

View File

@@ -1,7 +1,6 @@
package mc.core.exception;
public class ResourceUnloadedException extends McCoreUncheckedException {
public class ResourceUnloadedException extends RuntimeException {
public ResourceUnloadedException(String msg) {
super(msg);
}

View File

@@ -14,6 +14,12 @@ public interface World {
EntityLocation getSpawn();
void setSpawn(EntityLocation location);
default void setSpawn(double x, double y, double z, float yaw, float pitch) {
setSpawn(new EntityLocation(x, y, z, yaw, pitch));
}
default void setSpawn(double x, double y, double z) {
setSpawn(x, y, z, 0f, 0f);
}
Chunk getChunk(int x, int z);
void setChunk(int x, int z, Chunk chunkSection);

View File

@@ -1,7 +1,6 @@
package mc.core.world.chunk;
import mc.core.world.Biome;
import mc.core.world.World;
public interface Chunk {
int getX();
@@ -12,7 +11,4 @@ public interface Chunk {
Biome getBiome(int localX, int localZ);
void setBiome(int localX, int localZ, Biome biome);
World getWorld();
void setWorld(World world);
}

View File

@@ -0,0 +1,8 @@
package mc.core.world.chunk;
public interface ChunkProvider {
Chunk getChunk(int x , int z);
void saveChunk(Chunk chunk);
void saveChunk(Chunk... chunks);
}

View File

@@ -5,7 +5,6 @@
package mc.core.world.chunk;
import mc.core.world.Biome;
import mc.core.world.World;
import mc.core.world.block.Block;
/* 16x16x16 */
@@ -24,6 +23,4 @@ public interface ChunkSection {
void setAddition(int x, int y, int z, int value);
Biome getBiome(int localX, int localZ);
World getWorld();
}

View File

@@ -5,8 +5,7 @@ import org.junit.jupiter.api.Test;
import java.util.concurrent.ThreadLocalRandom;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.*;
class EntityLocationTest {
private static final ThreadLocalRandom rnd = ThreadLocalRandom.current();
@@ -42,6 +41,7 @@ class EntityLocationTest {
EntityLocation locOrig = new EntityLocation(x, y, z, yaw, pitch);
EntityLocation locClone = locOrig.clone();
assertEquals(locOrig, locClone);
assertNotSame(locOrig, locClone);
}
@Test

View File

@@ -1,19 +0,0 @@
# Flat world
Плоский мир
## Spring bean
```xml
<bean id="flatWorld" class="mc.world.flat.FlatWorld">
<property name="spawn">
<bean class="mc.core.Location">
<constructor-arg index="0" type="double" value="8"/>
<constructor-arg index="1" type="double" value="6"/>
<constructor-arg index="2" type="double" value="8"/>
</bean>
</property>
</bean>
```
`spawn` - точка спавна

View File

@@ -12,6 +12,7 @@ import mc.core.player.PlayerManager;
import mc.core.player.PlayerSettings;
import mc.core.world.World;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
@@ -26,8 +27,6 @@ public class H2PlayerManager implements PlayerManager {
@Autowired
private H2PlayerService h2PlayerService;
private List<H2Player> playerList = Collections.synchronizedList(new ArrayList<>());
@Autowired
private World world; //FIXME
@Override
public Player createPlayer(String name, EntityLocation location, World world) {
@@ -91,25 +90,9 @@ public class H2PlayerManager implements PlayerManager {
@Override
public Player getOfflinePlayer(String name) {
//TODO похоже в попытке где-то оптимизировать/сэконопить я сам себя ******[обманул]
//необходимо этот участок кода переписать
//потому как похоже на экономию на спичках
H2Player h2Player = playerList.stream()
return playerList.stream()
.filter(player -> player.getName().equals(name))
.filter(player -> !player.isOnline())
.findFirst().orElse(null);
if (h2Player == null) {
h2Player = h2PlayerService.getByName(name);
if (h2Player != null) {
h2Player.setWorld(world);
return h2Player;
} else {
return null;
}
} else {
h2Player.setWorld(world);
return h2Player;
}
.findFirst().orElseGet(() -> h2PlayerService.getByName(name));
}
}

View File

@@ -4,7 +4,9 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import mc.core.EntityLocation;
import mc.core.h2db.H2Player;
import mc.core.world.World;
import org.hibernate.annotations.GenericGenerator;
import org.springframework.context.ApplicationContext;
import javax.persistence.*;
import java.util.UUID;
@@ -55,11 +57,7 @@ public class H2PlayerEntity {
this.locationZ = player.getLocation().getZ();
this.locationYaw = player.getLocation().getYaw();
this.locationPitch = player.getLocation().getPitch();
if (player.getWorld() != null) { //FIXME
this.locationWorld = player.getWorld().getName();
} else {
this.locationWorld = "null_world";
}
}
public void setUuid(String uuid) {
@@ -78,12 +76,12 @@ public class H2PlayerEntity {
}
}
public H2Player toPlayer() {
public H2Player toPlayer(ApplicationContext context) {
H2Player player = new H2Player();
return toPlayer(player);
return toPlayer(player, context);
}
public H2Player toPlayer(H2Player player) {
public H2Player toPlayer(H2Player player, ApplicationContext context) {
player.setId(this.id.intValue());
player.setUuid(UUID.fromString(this.uuid));
player.setName(this.name);
@@ -97,7 +95,7 @@ public class H2PlayerEntity {
player.getLocation().setYawPitch(this.locationYaw, this.locationPitch);
}
player.setWorld(null); //FIXME
player.setWorld(context.getBean(this.locationWorld, World.class));
return player;
}

View File

@@ -4,12 +4,15 @@ import mc.core.h2db.H2Player;
import mc.core.h2db.entity.H2PlayerEntity;
import mc.core.h2db.repository.H2PlayerEntityRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import java.util.Optional;
@Service
public class H2PlayerServiceImpl implements H2PlayerService {
@Autowired
private ApplicationContext context;
@Autowired
private H2PlayerEntityRepository h2PlayerEntityRepository;
@@ -19,7 +22,7 @@ public class H2PlayerServiceImpl implements H2PlayerService {
//TODO возможно имеет смысл здесь оптимизация
//вместо toPlayer() сделать toPlayer(H2Player) который в существующий
//будет дописывать/обновлять данные
return h2PlayerEntityRepository.saveAndFlush(entity).toPlayer(player);
return h2PlayerEntityRepository.saveAndFlush(entity).toPlayer(player, context);
}
@Override
@@ -30,12 +33,12 @@ public class H2PlayerServiceImpl implements H2PlayerService {
@Override
public H2Player getByName(String name) {
Optional<H2PlayerEntity> optEntity = h2PlayerEntityRepository.findByName(name);
return optEntity.map(H2PlayerEntity::toPlayer).orElse(null);
return optEntity.map(entiry -> entiry.toPlayer(context)).orElse(null);
}
@Override
public H2Player getById(int id) {
Optional<H2PlayerEntity> optEntity = h2PlayerEntityRepository.findById((long) id);
return optEntity.map(H2PlayerEntity::toPlayer).orElse(null);
return optEntity.map(entiry -> entiry.toPlayer(context)).orElse(null);
}
}

View File

@@ -44,10 +44,10 @@ public class TestSpringConfig {
return properties;
}
@Bean
@Bean("mockWorld")
public World mockWorld() {
World mockWorld = mock(World.class);
when(mockWorld.getName()).thenReturn("mock_world");
when(mockWorld.getName()).thenReturn("mockWorld");
return mockWorld;
}

View File

@@ -3,6 +3,7 @@ package mc.core.h2db.service;
import mc.core.EntityLocation;
import mc.core.h2db.H2Player;
import mc.core.h2db.TestSpringConfig;
import mc.core.world.World;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
@@ -21,6 +22,8 @@ import static org.junit.jupiter.api.Assertions.*;
class H2PlayerServiceTest {
@Autowired
private H2PlayerService h2PlayerService;
@Autowired
private World world;
private H2Player buildPlayer() {
final ThreadLocalRandom rnd = ThreadLocalRandom.current();
@@ -38,18 +41,26 @@ class H2PlayerServiceTest {
rnd.nextFloat() * (maxF - minF) + minF,
rnd.nextFloat() * (maxF - minF) + minF
));
player.setWorld(null); //FIXME
player.setWorld(world);
return player;
}
private void assertPlayers(H2Player expected, H2Player actual) {
assertEquals(expected, actual);
assertEquals(expected.getName(), actual.getName());
assertEquals(expected.getLocation(), actual.getLocation());
assertNotNull(actual.getWorld());
assertEquals(expected.getWorld(), actual.getWorld());
}
@Test
void save() {
H2Player player = buildPlayer();
H2Player savedPlayer = h2PlayerService.save(player);
player.setId(savedPlayer.getId()); //FIXME костыль, однако
assertEquals(player, savedPlayer);
assertPlayers(player, savedPlayer);
}
@Test
@@ -111,7 +122,7 @@ class H2PlayerServiceTest {
H2Player player = h2PlayerService.save(buildPlayer());
H2Player player2 = h2PlayerService.getByName(player.getName());
assertEquals(player, player2);
assertPlayers(player, player2);
}
@Test
@@ -134,7 +145,7 @@ class H2PlayerServiceTest {
H2Player player = h2PlayerService.save(buildPlayer());
H2Player player2 = h2PlayerService.getById(player.getId());
assertEquals(player, player2);
assertPlayers(player, player2);
}
@Test

View File

@@ -100,6 +100,7 @@ public class ChunkDataPacket implements SCPacket {
netStream.writeInt(z); // Chunk Y
netStream.writeBoolean(initChunk); // Init Chunk
int maxH = 0;
if (sectionList == null && chunk != null) {
int bitMask = 0;
for (int h = 15; h >= 0; h--) {
@@ -107,6 +108,7 @@ public class ChunkDataPacket implements SCPacket {
ChunkSection chunkSection = chunk.getChunkSection(h);
if (chunkSection != null && chunkSection.getY() == h) {
bitMask |= 0x01;
maxH++;
} else {
bitMask |= 0x00;
}
@@ -121,6 +123,7 @@ public class ChunkDataPacket implements SCPacket {
ChunkSection chunkSection = sectionList.get(i);
if (chunkSection != null && chunkSection.getY() == h) {
bitMask |= 0x01;
maxH++;
} else {
bitMask |= 0x00;
}
@@ -136,7 +139,7 @@ public class ChunkDataPacket implements SCPacket {
int dataItems = 0;
final int airBlockPalette = serializeBlockState(BlockType.AIR);
for (int h = 0; h < 16; h++) {
for (int h = 0; h < maxH; h++) {
ChunkSection chunkSection = null;
if (chunk != null) {

View File

@@ -1,7 +1,7 @@
rootProject.name = 'mc-server'
include('core') // Core
include('flat_world')
include('simple_world')
include('h2_playermanager')
include('vanilla_commands')
include('proto_1.12.2') // Protocol 1.12.2

39
simple_world/README.MD Normal file
View File

@@ -0,0 +1,39 @@
# Simple world
Простая реализация мира
## Spring bean
```xml
<bean id="simpleWorld" class="mc.world.simple.SimpleWorld">
<property name="spawn">
<bean class="mc.core.EntityLocation">
<constructor-arg index="0" type="double" value="8"/>
<constructor-arg index="1" type="double" value="6"/>
<constructor-arg index="2" type="double" value="8"/>
<constructor-arg index="3" type="float" value="0"/>
<constructor-arg index="4" type="float" value="0"/>
<constructor-arg index="5" type="mc.core.world.World">
<null/>
</constructor-arg>
</bean>
</property>
<property name="layersBlock">
<list value-type="java.lang.String">
<value>1;BEDROCK</value>
<value>2;DIRT</value>
<value>1;GRASS</value>
</list>
</property>
</bean>
```
`spawn` - точка спавна.
При указании точки спавна, указывать шестой параметр `World` не имеет смысла,
т.к. `SimpleWorld` всё равно перезапишет этот параметр.
`layersBlock` - слои блоков.
В качестве значения указывается спиток строк, каждая из которых описывает слой блоков.
Формат строк такой: `кол-во_слоёв;тип_блока`. Порядок строк такой: сверху нижние слои, а снизу - верхние.

View File

@@ -0,0 +1,55 @@
package mc.world.simple;
import mc.core.world.block.BlockType;
import mc.core.world.chunk.Chunk;
import mc.core.world.chunk.ChunkProvider;
import mc.core.world.chunk.ChunkSection;
import java.util.ArrayList;
import java.util.List;
public class FlatChunkProvider implements ChunkProvider {
private ChunkSection chunkSection;
public void setLayersBlockAsString(List<String> listOfLayers) {
List<BlockType> layoutsBlock = new ArrayList<>();
for (String value : listOfLayers) {
String[] splitValue = value.split(";");
BlockType blockType;
try {
blockType = BlockType.valueOf(splitValue[1]);
} catch (IllegalArgumentException e) {
continue;
}
for (int i = 0; i < Integer.parseInt(splitValue[0]); i++) {
layoutsBlock.add(blockType);
}
}
setLayersBlock(layoutsBlock);
}
public void setLayersBlock(List<BlockType> layoutsBlock) {
this.chunkSection = new SimpleChunkSection(layoutsBlock);
}
@Override
public Chunk getChunk(int x, int z) {
Chunk chunk = new SimpleChunk(x, z);
chunk.setChunkSection(0, chunkSection);
return chunk;
}
@Override
public void saveChunk(Chunk chunk) {
//FIXME nope...
}
@Override
public void saveChunk(Chunk... chunks) {
//FIXME nope...
}
}

View File

@@ -1,29 +1,20 @@
package mc.world.flat;
package mc.world.simple;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import mc.core.world.Biome;
import mc.core.world.World;
import mc.core.world.chunk.Chunk;
import mc.core.world.chunk.ChunkSection;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
@Slf4j
@RequiredArgsConstructor
public class SimpleChunk implements Chunk {
@Getter
private int x, z;
private Reference<World> refWorld;
private final int x, z;
private ChunkSection chunkSection;
private final Biome biome = Biome.PLAINS;
public SimpleChunk(int x, int z, World world) {
this.x = x;
this.z = z;
setWorld(world);
}
@Override
public ChunkSection getChunkSection(int height) {
return chunkSection;
@@ -43,19 +34,4 @@ public class SimpleChunk implements Chunk {
public void setBiome(int localX, int localZ, Biome biome) {
// ignore
}
@Override
public World getWorld() {
if (refWorld.get() == null) {
log.error("World unloaded?");
return null;
} else {
return refWorld.get();
}
}
@Override
public void setWorld(World world) {
this.refWorld = new WeakReference<>(world);
}
}

View File

@@ -1,17 +1,21 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-04-28
*/
package mc.world.flat;
package mc.world.simple;
import mc.core.world.Biome;
import mc.core.world.World;
import mc.core.world.block.Block;
import mc.core.world.block.BlockFactory;
import mc.core.world.block.BlockType;
import mc.core.world.chunk.ChunkSection;
import java.util.List;
public class SimpleChunkSection implements ChunkSection {
private final BlockFactory blockFactory = new BlockFactory();
private final List<BlockType> layersBlock;
public SimpleChunkSection(List<BlockType> layersBlock) {
this.layersBlock = layersBlock;
}
@Override
public int getSkyLight(int x, int y, int z) {
if (y <= 3) return 0;
@@ -57,16 +61,20 @@ public class SimpleChunkSection implements ChunkSection {
@Override
public Block getBlock(int x, int y, int z) {
BlockFactory blockFactory = new BlockFactory();
if (x < 0) x = 0;
else if (x > 15) x = 15;
if (y < 0) y = 0;
else if (y > 15) y = 15;
if (z < 0) z = 0;
else if (z > 15) z = 15;
if (y == 0) return blockFactory.create(BlockType.BEDROCK, x, y, z);
else if (y >= 1 && y <= 2) return blockFactory.create(BlockType.DIRT, x, y, z);
else if (y == 3) return blockFactory.create(BlockType.GRASS, x, y, z);
else return blockFactory.create(BlockType.AIR, x, y, z);
if (y >= layersBlock.size()) {
return blockFactory.create(BlockType.AIR, x, y, z);
}
@Override
public World getWorld() {
return null;
BlockType blockType = layersBlock.get(y);
if (blockType == null) return blockFactory.create(BlockType.AIR, x, y, z);
return blockFactory.create(blockType, x, y, z);
}
}

View File

@@ -1,8 +1,4 @@
/*
* DmitriyMX <dimon550@gmail.com>
* 2018-04-28
*/
package mc.world.flat;
package mc.world.simple;
import lombok.Getter;
import lombok.Setter;
@@ -11,37 +7,50 @@ import mc.core.EntityLocation;
import mc.core.world.World;
import mc.core.world.WorldType;
import mc.core.world.chunk.Chunk;
import mc.core.world.chunk.ChunkSection;
import mc.core.world.chunk.ChunkProvider;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.stereotype.Component;
import javax.annotation.Nonnull;
@Slf4j
public class FlatWorld implements World {
@Component
public class SimpleWorld implements World, BeanNameAware {
@Getter
private final String name = "flat";
private String name;
@Getter
private final WorldType worldType = WorldType.FLAT;
@Setter
private EntityLocation spawn;
private ChunkSection chunkSection = new SimpleChunkSection();
@Setter
private ChunkProvider chunkProvider;
@Override
public EntityLocation getSpawn() {
if (this.spawn == null) {
log.warn("Spawn is not defined! Set spawn [0, 6, 0]");
this.spawn = new EntityLocation(0d, 6d, 0d, 0f, 0f);
setSpawn(0d, 6d, 0d);
}
return this.spawn;
}
@Override
public void setSpawn(EntityLocation location) {
this.spawn = location;
}
@Override
public Chunk getChunk(int x, int z) {
Chunk chunk = new SimpleChunk(x, z, this);
chunk.setChunkSection(0, chunkSection);
return chunk;
return chunkProvider.getChunk(x, z);
}
@Override
public void setChunk(int x, int z, Chunk chunk) {
throw new UnsupportedOperationException();
}
@Override
public void setBeanName(@Nonnull String name) {
this.name = name;
}
}

View File

@@ -0,0 +1,44 @@
package mc.world.simple;
import com.google.common.collect.Lists;
import mc.core.world.block.Block;
import mc.core.world.block.BlockType;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
class SimpleChunkSectionTest {
private SimpleChunkSection chunkSection;
private List<BlockType> layersBlock;
@BeforeEach
void before() {
layersBlock = Lists.newArrayList(
BlockType.BEDROCK,
BlockType.DIRT,
BlockType.DIRT,
BlockType.GRASS
);
chunkSection = new SimpleChunkSection(layersBlock);
}
@Test
void getBlock() {
for (int y = 15; y >= 0; y--) {
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
Block block = chunkSection.getBlock(x, y, z);
if (y > layersBlock.size()-1) {
assertEquals(block.getBlockType(), BlockType.AIR);
} else {
assertEquals(block.getBlockType(), layersBlock.get(y));
}
}
}
}
}
}

View File

@@ -0,0 +1,37 @@
package mc.world.simple;
import mc.core.EntityLocation;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.junit.jupiter.api.Assertions.*;
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {TestSpringConfig.class})
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
class SimpleWorldTest {
@Autowired
private SimpleWorld world;
@Test
void spawn() {
final EntityLocation location = new EntityLocation(1d, 2d, 3d,4f, 5f);
world.setSpawn(location);
assertEquals(location, world.getSpawn());
assertSame(location, world.getSpawn());
world.setSpawn(1d, 2d, 3d, 4f, 5f);
assertEquals(location, world.getSpawn());
assertNotSame(location, world.getSpawn());
location.setYawPitch(0, 0);
world.setSpawn(1d, 2d, 3d);
assertEquals(location, world.getSpawn());
assertNotSame(location, world.getSpawn());
}
}

View File

@@ -0,0 +1,38 @@
package mc.world.simple;
import com.google.common.collect.Lists;
import mc.core.world.block.BlockType;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import java.util.List;
@Configuration
@ComponentScan("mc.world.simple")
public class TestSpringConfig {
@Bean
public List<BlockType> layersBlock() {
return Lists.newArrayList(
BlockType.BEDROCK,
BlockType.DIRT,
BlockType.DIRT,
BlockType.GRASS
);
}
@Bean
public SimpleChunkSection chunkSection(List<BlockType> layersBlock) {
return new SimpleChunkSection(layersBlock);
}
@Bean
public SimpleWorld simpleWorld(List<BlockType> layersBlock) {
FlatChunkProvider chunkProvider = new FlatChunkProvider();
chunkProvider.setLayersBlock(layersBlock);
SimpleWorld world = new SimpleWorld();
world.setChunkProvider(chunkProvider);
return world;
}
}