Minecraft Mod – Project Skirmish

The Skirmish Minecraft Mod and Plugin is a long term project which I have been working on to recreate the game rust within a free to play Minecraft server. Skirmish includes a new building system, with functional gravity, a custom loot generation system and many more features such as custom blocks and items all done within a single Mod and Plugin. There have been many attempts by other people to do this effectively in the past, however most fall short in quality of models or with lacking peformance on their servers.

Skirmish Features And Progress

Skirmish currently features a custom building and stability system handled in a server side plugin, this forces the player to build in a way similar to the game Rust and simulates gravitational physics on building. Recently I added a new crate generation system allowing for crates with randomly generated items to be spawned in set locations around the map, these regenerate whenever they are emptied and are saved on server stop and restart. Skirmish also features discord integration allowing people to view their statistics on the server such as playtime, kills and deaths.

Skirmish Discord integration showing player statistics

The Skirmish Stability System

Within the game Minecraft there is no gravitational or stability physics on placed blocks, this allows users to infinitely expand their structures without worrying about making them stable. This is entirely different to the game Rust where there is realistic building physics requiring the player to build in certain ways, this is a core component of the game is it allows for freedom and creativity within designing bases, but keeps them within a reasonable limit for other players to try and attack. I wish to implement a similar system to Rust within Skirmish with a few of my own alterations. My process of doing this is explained below.

Structure Placement-

Within the game Rust everything you build has to be off of a single foundation unit, from this you can expand in all directions for as far as your stability will allow, the further out you go the less stable your structures are and they will collapse. You can increase stability by adding more foundations.

To recreate this in Minecraft I first made it so that foundations must be the first block which you place, I decided to handle all of this with a server side plugin using the Spigot API as this gives me a lot of freedom without the concern of players exploiting things from their own client. First I made a new class implementing the Spigot Listener class, this allows me to run methods automatically based on events that happen in game such as whenever a block is placed. To make it so that the player can only place things off of foundations I first check if the block placed is on top of a foundation (stone bricks), if it is not the block being placed must be a foundation.

JavaScript
    @EventHandler
    public void onPlace(BlockPlaceEvent event ){
        //getting relevant info from event
        final Block block = event.getBlock();
        final Player player = event.getPlayer();
        final Block placedAgainst = event.getBlockAgainst();
        final boolean canBuild = event.canBuild();
        Location placedBlockLocation = block.getLocation();
        int[] placedCords = new int[]{placedBlockLocation.getBlockX(),placedBlockLocation.getBlockY(),placedBlockLocation.getBlockZ()};

        //checks if place logic returns true
        if(checkBuild(placedCords, block, placedAgainst, player)){

            PlayerInventory pi = (PlayerInventory)event.getPlayer().getInventory();

            if(block.getType() == Material.STONE && pi.containsAtLeast(new ItemStack(Material.DIAMOND), 2)){
                pi.setItemInMainHand(new ItemStack(Material.STONE));
                pi.removeItem(new ItemStack(Material.DIAMOND, 2));
                return;
            }
            if(block.getType() == Material.SMOOTH_BRICK && pi.containsAtLeast(new ItemStack(Material.DIAMOND), 3)){
                pi.setItemInMainHand(new ItemStack(Material.SMOOTH_BRICK));
                pi.removeItem(new ItemStack(Material.DIAMOND, 3));
                return;
            }

This code makes it so that whenever a block is placed the block below it is checked, if it is not being placed on a foundation it checks if the block placed is a foundation, if it is the required resources are then taken from the players inventory and the block place is executed. This makes up the core of my stability system. I also implemented walls, these can be placed infinitely high as long as they are placed on top of a foundation or another wall. This uses the same code as the foundation checks. Following this I begun on the more complicated part which is floors (these also act as ceilings). I decided to allow these to be placed in a specific pattern allowing them to extend off of a wall by two blocks (the units of distance used in Minecraft) and by one block when extended diagonally. This was done using the code below.

JavaScript
        //place floors
        if(block.getType() == Material.STEP){
            //initial checks to see if on wall this is directly on x and z by 1
            if(placedAgainst.getType() == Material.STONE){
                return true;
            }
            //directly on x and z by 2
            if(p.getLocation().getWorld().getBlockAt(placedCords[0]+2,placedCords[1],placedCords[2]).getType() == Material.STONE){
                return true;
            }
            if(p.getLocation().getWorld().getBlockAt(placedCords[0]-2,placedCords[1],placedCords[2]).getType() == Material.STONE){
                return true;
            }
            if(p.getLocation().getWorld().getBlockAt(placedCords[0],placedCords[1],placedCords[2]+2).getType() == Material.STONE){
                return true;
            }
            if(p.getLocation().getWorld().getBlockAt(placedCords[0],placedCords[1],placedCords[2]-2).getType() == Material.STONE){
                return true;
            }
            //diagonally by 1
            if(p.getLocation().getWorld().getBlockAt(placedCords[0]-1,placedCords[1],placedCords[2]-1).getType() == Material.STONE){
                return true;
            }
            if(p.getLocation().getWorld().getBlockAt(placedCords[0]-1,placedCords[1],placedCords[2]+1).getType() == Material.STONE){
                return true;
            }
            if(p.getLocation().getWorld().getBlockAt(placedCords[0]+1,placedCords[1],placedCords[2]+1).getType() == Material.STONE){
                return true;
            }
            if(p.getLocation().getWorld().getBlockAt(placedCords[0]+1,placedCords[1],placedCords[2]-1).getType() == Material.STONE){
                return true;
            }
            else{
                return false;
            }
        }

This code checks the block which the floor (the step material) was placed on, and then checks the blocks around it to make sure that it is considered stable, if it does not pass the checks the block placement is cancelled preventing the player from placing things which would be considered unstable. This concludes the building and stability system in its current state and a video is below showing its functionality.

Skirmish Raiding And Repairing

A core element of the game Rust is the raiding system, this allows players to attack and destroy other peoples bases, to take their items. I will need to implement this effectively for my game to be an accurate and fun recreation.

To implement this within my project I need to implement structure health, damage, and a way for the player to repair their structures after damaged. First I made a system to work out the direction which the explosive block was placed, this will work out what blocks will actually be damaged by the explosion since there is no way to do a sphere collider check like you might within engines such as unity.

Direction checking-

First I made a check direction method, this would calculate the direction relative from the explosive in which to damage the structure. I did this by checking what the block was placed against and then checking the blocks around it using the method shown below.

JavaScript
    static String GetDirection(int[] placedCords, Block placedAgainst){
        //gets placed against cords
        Location placedAgainstLocation = placedAgainst.getLocation();
        int[] placedAgainstCords = new int[]{placedAgainstLocation.getBlockX(),placedAgainstLocation.getBlockY(),placedAgainstLocation.getBlockZ()};


        //checks X Axis
        if(placedAgainstCords[0] > placedCords[0]){
            System.out.println("West");
            return "West";
        }
        if(placedAgainstCords[0] < placedCords[0]){
            System.out.println("East");
            return "East";
        }

        //checks Y axis
        if(placedAgainstCords[1] > placedCords[1]){
            System.out.println("Bottom");
            return "Bottom";
        }
        if(placedAgainstCords[1] < placedCords[1]){
            System.out.println("Top");
            return "Top";
        }

        //checks Z axis
        if(placedAgainstCords[2] > placedCords[2]){
            System.out.println("North");
            return "North";
        }
        if(placedAgainstCords[2] < placedCords[2]){
            System.out.println("South");
            return "South";
        }
        return "Failed";
    }

This code allowed me to easily work out what direction I needed to damage with the explosive, next I need to work on the damaging part itself. For this I begin by scanning the blocks in a 3x3 radius in the direction which damage will be dealt. The explosive will damage a maximum of a 3x3x1 block area so as to make it balanced and relatively simple to defend against. To do this I will locate all the blocks within that radius and check their types, if they are a placed block such as a wall I will do damage to them, this will be done by changing them to the damaged block state

JavaScript
        if (direction == "North" || direction == "South"){
            //north and south
            for (int i = 0; i <= 8;) {
                x++;

                //top left block
                Material mat = p.getLocation().getWorld().getBlockAt(placedCords[0] + x, placedCords[1] + y, placedCords[2] + z).getType();

                //Do an or statement for all of wood, so wall, floor, foundy. This reduces the checks needed, also add or statements for all damage states
                //this will need to be done 4 times for each material

                //full health
                if (mat == Material.STONE) {
                    System.out.println("BlockFound");
                    surroundingBlocks[i] = 1;
                    blockStages[i] = 1;
                }
                //half health
                if(mat == Material.COBBLESTONE){
                    System.out.println("BlockFound");
                    surroundingBlocks[i] = 1;
                    blockStages[i] = 2;
                }

                if (x == 1) {
                    x = -2;
                    y--;
                }

                i++;
            }

The code above loops through the radius of potential blocks and adds them to an array if a player made blocks is found, it also stores that blocks current damage state within an array for later when I will actually damage the blocks. I had to repeat this method for the east and west directions, and the up and down directions. Changing the blocks checked slightly so that it functions as intended. Next I will work on the actual damaging of blocks and their destruction, along with adding in a timer to the explosive device and some effects to make it more enjoyable to use.

Block damaging-

Damaging the actual blocks is relatively simple, however with damage comes the repair system. I will need to preemptively implement a system to log what blocks have been damaged or broken by the explosive for later use.

JavaScript
            for (int i1 = 0; i1 <= 8;) {
                x1++;

                //checks its not tryna set air
                if(surroundingBlocks[i1] != 0){
                    //means just broken, should never be called
                    if(blockStages[i1] == 3){

                    }
                    //half health
                    if(blockStages[i1] == 2){
                        //set to air (broken)
                        p.getLocation().getWorld().getBlockAt(placedCords[0] + x1, placedCords[1] + y1, placedCords[2] + z1).setType(Material.AIR);
                        Location placedBlock = new Location(p.getLocation().getWorld(), placedCords[0] + x1, placedCords[1] + y1, placedCords[2] + z1);
                        AntiBuild.addKey(placedBlock);
                        //increase damage percentage (to broken)
                        blockStages[i1] += 1;

                    }
                    //full health
                    if(blockStages[i1] == 1){
                        //set to damaged material
                        p.getLocation().getWorld().getBlockAt(placedCords[0] + x1, placedCords[1] + y1, placedCords[2] + z1).setType(Material.COBBLESTONE);
                        Location placedBlock = new Location(p.getLocation().getWorld(), placedCords[0] + x1, placedCords[1] + y1, placedCords[2] + z1);
                        AntiBuild.addKey(placedBlock);
                        //increase damage percentage
                        blockStages[i1] += 1;

                    }
                    //no block set meaning its terrain
                    if(blockStages[i1] == 0){

                    }

                }

                if (x1 == 1) {
                    x1 = -2;
                    y1--;
                }

                i1++;
            }

The code above indexes through the arrays created previously and replaces the previous blocks with new ones corresponding to their damage states, this uses similar code to the system for working out what blocks to damage so was very simple to implement, I also need to log this damage in a hashmap to be used within the repairing system.

Repairing-

Within the game Rust if a structure is damaged and then left for a while the player can use resources to repair it, it has to be left for a while otherwise bases would be near impossible to raid without huge teams and would generally unbalance the game. To do this I will make several new functions along with a hashmap to store all of this information.

JavaScript
public final class AntiBuild extends JavaPlugin {
    //location, true = can place, false = can not place
    public static Map<Location, Integer> locations = new HashMap<Location, Integer>();

    //foreach value in hashmap if int != 0, int--. If == 0, remove from map
    //nothing can be repaired until the end of the timer, however blocks can be placed there half way through, altho they will be on lower health. (20s for repairing or place at full health, 10s for low health placing)


    //checks every 10 ticks for damage change (.5s)
    public static void checkMap() {
        System.out.println("c2");
        if(!locations.isEmpty()){
            try {
                for (Map.Entry<Location, Integer> entry : locations.entrySet()) {
                    Location key = entry.getKey();
                    Integer value = entry.getValue();
                    System.out.println("c2");
                    //counts down the value
                    if (value > 0) {
                        value--;
                        locations.put(key, value);
                    }
                    //removes item from map after timer
                    else {
                        locations.remove(key);
                    }
                }
            }catch (Exception ignored){

            }
        }
    }

First I declare the hashMap, I decided to use this data storage method due to its ease of use and versatility, it is an excellent alternative to the multi dimensional arrays and lists which wouldve had to be used otherwise. Next I check every 10 in game ticks (which is around 0.5 seconds depending on server latency) to see if anything has changed in this hashmap. If the map is not empty this code gradually reduces the damaged timer and then removes it after the timer has completed. This method is used in conjunction with my methods for adding keys and finally checking the upgradeability and repairability of a block.

JavaScript
    public static void addKey(Location key){
        locations.put(key, 40);
    }

    //this is only used to check placability
    public static boolean checkBuild(Location key){
        if(locations.containsKey(key)){
            if(locations.get(key) != null){
                System.out.println("Recently broken or damaged");
                return false;
            }
            else{
                System.out.println("Not recently broken or damaged");
                return true;
            }
        }
        else{
            System.out.println("Not recently broken or damaged");
            return true;
        }
    }

Above are my methods for adding values to the hashmap along with my function to check if the player can build. The first method adds a value to the hashmap, this is extremely simple and can be accessed from any scripts requiring it that I may make in the future. Next I made the system to check whether or not the location a player is trying to build was recently destroyed, this prevents players from immediately replacing blocks that are broken during raids by checking to see if they are stored within the hashmap of damaged blocks. This concludes the raiding and repairing system in its current state and a video is below showing its functionality.

Challenges And Skills Gained From Skirmish

Skirmish was my first Java project and my first time using various Api's such as Discord, Bukkit, Spigot and Forge. It was incredibly challenging to pick all of these up in the two months which I have been working on Skirmish so far, however I have managed to push through with little to no assistance and have been able to make a stable and production ready project. Generally I am very proud of my progress on this so far and am excited to see where it might end up, presently it has been put aside whilst I focus on college assignments but I cant wait to get back to it!

A section of code from the Skirmish loot generation system