├── settings.gradle
├── src
└── main
│ ├── resources
│ ├── char.png
│ └── terrain.png
│ └── java
│ └── com
│ └── mojang
│ └── minecraft
│ ├── HitResult.java
│ ├── level
│ ├── LevelListener.java
│ ├── tile
│ │ ├── GrassTile.java
│ │ ├── Bush.java
│ │ └── Tile.java
│ ├── DirtyChunkSorter.java
│ ├── PerlinNoiseFilter.java
│ ├── Tessellator.java
│ ├── Chunk.java
│ ├── LevelRenderer.java
│ ├── Level.java
│ └── Frustum.java
│ ├── character
│ ├── Vec3.java
│ ├── Polygon.java
│ ├── Vertex.java
│ ├── ZombieModel.java
│ ├── Zombie.java
│ └── Cube.java
│ ├── Player.java
│ ├── Timer.java
│ ├── MinecraftApplet.java
│ ├── particle
│ ├── ParticleEngine.java
│ └── Particle.java
│ ├── Textures.java
│ ├── Entity.java
│ ├── phys
│ └── AABB.java
│ └── Minecraft.java
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── metadata.json
├── .idea
└── runConfigurations
│ └── Run_Client.xml
├── README.md
├── gradlew.bat
├── .gitignore
└── gradlew
/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'minecraft'
--------------------------------------------------------------------------------
/src/main/resources/char.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thecodeofnotch/mc-161807/HEAD/src/main/resources/char.png
--------------------------------------------------------------------------------
/src/main/resources/terrain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thecodeofnotch/mc-161807/HEAD/src/main/resources/terrain.png
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thecodeofnotch/mc-161807/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/metadata.json:
--------------------------------------------------------------------------------
1 | {
2 | "timestampRelease": 1242490020000,
3 | "commitMessage": "rename RubyDung to Minecraft, add applet to run the game"
4 | }
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/src/main/java/com/mojang/minecraft/HitResult.java:
--------------------------------------------------------------------------------
1 | package com.mojang.minecraft;
2 |
3 | public class HitResult {
4 |
5 | public int type;
6 |
7 | public int x;
8 | public int y;
9 | public int z;
10 |
11 | public int face;
12 |
13 | /**
14 | * Target tile over mouse
15 | *
16 | * @param type Type of result
17 | * @param x Tile position x
18 | * @param y Tile position y
19 | * @param z Tile position z
20 | * @param face Face id of the tile
21 | */
22 | public HitResult(int type, int x, int y, int z, int face) {
23 | this.type = type;
24 | this.x = x;
25 | this.y = y;
26 | this.z = z;
27 | this.face = face;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/com/mojang/minecraft/level/LevelListener.java:
--------------------------------------------------------------------------------
1 | package com.mojang.minecraft.level;
2 |
3 | public interface LevelListener {
4 | /**
5 | * Gets called when a tile changed it's light level
6 | *
7 | * @param x Tile position x
8 | * @param z Tile position z
9 | * @param minY Minimum tile position Y (Start range)
10 | * @param maxY Maximum tile position Y (End range)
11 | */
12 | void lightColumnChanged(int x, int z, int minY, int maxY);
13 |
14 | /**
15 | * Gets called when a tile changed it's type
16 | *
17 | * @param x Tile position x
18 | * @param y Tile position y
19 | * @param z Tile position z
20 | */
21 | void tileChanged(int x, int y, int z);
22 |
23 | /**
24 | * Gets called when the entire level changed
25 | */
26 | void allChanged();
27 | }
--------------------------------------------------------------------------------
/.idea/runConfigurations/Run_Client.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
11 |
16 |
17 |
18 | true
19 | true
20 | false
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/main/java/com/mojang/minecraft/character/Vec3.java:
--------------------------------------------------------------------------------
1 | package com.mojang.minecraft.character;
2 |
3 | public class Vec3 {
4 |
5 | public float x;
6 | public float y;
7 | public float z;
8 |
9 | /**
10 | * Vector object containing three float values
11 | *
12 | * @param x X value
13 | * @param y Y value
14 | * @param z Z value
15 | */
16 | public Vec3(float x, float y, float z) {
17 | this.x = x;
18 | this.y = y;
19 | this.z = z;
20 | }
21 |
22 | /**
23 | * Create an interpolated vector from the current vector position to the given one
24 | *
25 | * @param vector The end vector
26 | * @param partialTicks Interpolation progress
27 | * @return Interpolated vector between the two positions
28 | */
29 | public Vec3 interpolateTo(Vec3 vector, float partialTicks) {
30 | float interpolatedX = this.x + (vector.x - this.x) * partialTicks;
31 | float interpolatedY = this.y + (vector.y - this.y) * partialTicks;
32 | float interpolatedZ = this.z + (vector.z - this.z) * partialTicks;
33 |
34 | return new Vec3(interpolatedX, interpolatedY, interpolatedZ);
35 | }
36 |
37 | /**
38 | * Set x, y and z of the vector
39 | *
40 | * @param x X value
41 | * @param y Y value
42 | * @param z Z value
43 | */
44 | public void set(float x, float y, float z) {
45 | this.x = x;
46 | this.y = y;
47 | this.z = z;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/com/mojang/minecraft/level/tile/GrassTile.java:
--------------------------------------------------------------------------------
1 | package com.mojang.minecraft.level.tile;
2 |
3 | import com.mojang.minecraft.level.Level;
4 |
5 | import java.util.Random;
6 |
7 | public class GrassTile extends Tile {
8 |
9 | /**
10 | * Create a grass tile with the id
11 | *
12 | * @param id The id of the grass tile
13 | */
14 | protected GrassTile(int id) {
15 | super(id);
16 |
17 | this.textureId = 3;
18 | }
19 |
20 | @Override
21 | protected int getTexture(int face) {
22 | // Texture mapping of the grass tile
23 | return face == 1 ? 0 : face == 0 ? 2 : 3;
24 | }
25 |
26 | @Override
27 | public void onTick(Level level, int x, int y, int z, Random random) {
28 | if (level.isLit(x, y, z)) {
29 | // Make surrounding dirt tiles to grass
30 | for (int i = 0; i < 4; ++i) {
31 | int targetX = x + random.nextInt(3) - 1;
32 | int targetY = y + random.nextInt(5) - 3;
33 | int targetZ = z + random.nextInt(3) - 1;
34 |
35 | // If target is dirt and has sunlight
36 | if (level.getTile(targetX, targetY, targetZ) == Tile.dirt.id && level.isLit(targetX, targetY, targetZ)) {
37 |
38 | // Set to grass
39 | level.setTile(targetX, targetY, targetZ, Tile.grass.id);
40 | }
41 | }
42 | } else {
43 | // Set tile to dirt if there is no sunlight
44 | level.setTile(x, y, z, Tile.dirt.id);
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # mc-161807 (Pre-Classic)
2 | Development phase: May 16, 2009 (until 18:07 UTC+2)
3 |
4 | ## Unofficial name
5 | While this version is known to exist it is missing from the launcher and has not been archived elsewhere, meaning that it is currently lost.
6 |
7 | The version name is taken from [Notchs IRC message](https://archive.org/download/Minecraft_IRC_Logs_2009/history/files/May-15-to-June-03-2009/2009-05-16.075419-0400EDT.txt.~1~)
8 | `` (12:07:06) notch: minecraft alpha is available. I need someone on windows, someone on mac and someone on linux!``
9 |
10 |
11 | Minecraft Wiki uses ``mc-161607`` for this version because it's in UTC format.
12 | ``mc-161807`` would be in UTC+2 (Timezone in Sweden)
13 |
14 | ## Changes
15 | - Renamed com.mojang.rubydung to com.mojang.minecraft
16 | - Added Applet to run the game
17 |
18 | ## Accuracy
19 | This version has only been sent to [dock](https://minecraft.gamepedia.com/Hayden_Scott-Baron) and [fartron](https://forums.tigsource.com/index.php?action=profile;u=61) for testing.
20 | There is no existing backup.
21 | We can assume that all necessary changes have been made in this update for the first release.
22 |
23 | ## References
24 | - [Minecraft Wiki - Java_Edition_pre-Classic_mc-161607](https://minecraft.gamepedia.com/Java_Edition_pre-Classic_mc-161607)
25 | - [IRC logs](https://archive.org/download/Minecraft_IRC_Logs_2009/history/files/May-15-to-June-03-2009/2009-05-16.075419-0400EDT.txt.~1~) to recreate the unreleased features:
26 | - ``[12:07:06] notch: minecraft alpha is available. I need someone on windows, someone on mac and someone on linux!``
27 |
28 | ## Setup
29 | 1. Clone the project
30 | 2. Execute the gradle task ``run``
--------------------------------------------------------------------------------
/src/main/java/com/mojang/minecraft/character/Polygon.java:
--------------------------------------------------------------------------------
1 | package com.mojang.minecraft.character;
2 |
3 | import static org.lwjgl.opengl.GL11.*;
4 |
5 | public class Polygon {
6 |
7 | public Vertex[] vertices;
8 | public int vertexCount;
9 |
10 | /**
11 | * Create polygon without UV mappings
12 | *
13 | * @param vertices Vertex array
14 | */
15 | public Polygon(Vertex[] vertices) {
16 | this.vertices = vertices;
17 | this.vertexCount = vertices.length;
18 | }
19 |
20 | /**
21 | * Bind UV mappings on the vertices
22 | *
23 | * @param vertices Vertex array
24 | * @param minU Minimum U coordinate
25 | * @param minV Minimum V coordinate
26 | * @param maxU Maximum U coordinate
27 | * @param maxV Maximum V coordinate
28 | */
29 | public Polygon(Vertex[] vertices, int minU, int minV, int maxU, int maxV) {
30 | this(vertices);
31 |
32 | // Map UV on vertices
33 | vertices[0] = vertices[0].remap(maxU, minV);
34 | vertices[1] = vertices[1].remap(minU, minV);
35 | vertices[2] = vertices[2].remap(minU, maxV);
36 | vertices[3] = vertices[3].remap(maxU, maxV);
37 | }
38 |
39 | public void render() {
40 | // Set color of polygon
41 | glColor3f(1.0F, 1.0F, 1.0F);
42 |
43 | // Render all vertices
44 | for (int i = 3; i >= 0; i--) {
45 | Vertex vertex = this.vertices[i];
46 |
47 | // Bind UV mappings
48 | glTexCoord2f(vertex.u / 64.0F, vertex.v / 32.0F);
49 |
50 | // Render vertex
51 | glVertex3f(vertex.position.x, vertex.position.y, vertex.position.z);
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/com/mojang/minecraft/character/Vertex.java:
--------------------------------------------------------------------------------
1 | package com.mojang.minecraft.character;
2 |
3 | public class Vertex {
4 |
5 | public Vec3 position;
6 |
7 | public float u;
8 | public float v;
9 |
10 | /**
11 | * A vertex contains a 3 float vector position and UV coordinates
12 | *
13 | * @param x X position
14 | * @param y Y position
15 | * @param z Z position
16 | * @param u U mapping
17 | * @param v V mapping
18 | */
19 | public Vertex(float x, float y, float z, float u, float v) {
20 | this(new Vec3(x, y, z), u, v);
21 | }
22 |
23 | /**
24 | * A vertex contains a 3 float vector position and UV coordinates
25 | *
26 | * @param vertex Vertex for the position
27 | * @param u U mapping
28 | * @param v V mapping
29 | */
30 | public Vertex(Vertex vertex, float u, float v) {
31 | this.position = vertex.position;
32 | this.u = u;
33 | this.v = v;
34 | }
35 |
36 | /**
37 | * A vertex contains a 3 float vector position and UV coordinates
38 | *
39 | * @param position Vector position
40 | * @param u U mapping
41 | * @param v V mapping
42 | */
43 | public Vertex(Vec3 position, float u, float v) {
44 | this.position = position;
45 | this.u = u;
46 | this.v = v;
47 | }
48 |
49 | /**
50 | * Create a new vertex of the current one with different UV mappings
51 | *
52 | * @param u New U mapping
53 | * @param v New V mapping
54 | * @return New vertex with the vector position of the current one
55 | */
56 | public Vertex remap(float u, float v) {
57 | return new Vertex(this, u, v);
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/java/com/mojang/minecraft/Player.java:
--------------------------------------------------------------------------------
1 | package com.mojang.minecraft;
2 |
3 | import com.mojang.minecraft.level.Level;
4 | import org.lwjgl.input.Keyboard;
5 |
6 | public class Player extends Entity {
7 |
8 | /**
9 | * The player that is controlling the camera of the game
10 | *
11 | * @param level Level of the player
12 | */
13 | public Player(Level level) {
14 | super(level);
15 |
16 | this.heightOffset = 1.62f;
17 | }
18 |
19 | @Override
20 | public void onTick() {
21 | super.onTick();
22 |
23 | float forward = 0.0F;
24 | float vertical = 0.0F;
25 |
26 | // Reset the position of the player
27 | if (Keyboard.isKeyDown(19)) { // R
28 | resetPosition();
29 | }
30 |
31 | // Player movement
32 | if (Keyboard.isKeyDown(200) || Keyboard.isKeyDown(17)) { // Up, W
33 | forward--;
34 | }
35 | if (Keyboard.isKeyDown(208) || Keyboard.isKeyDown(31)) { // Down, S
36 | forward++;
37 | }
38 | if (Keyboard.isKeyDown(203) || Keyboard.isKeyDown(30)) { // Left, A
39 | vertical--;
40 | }
41 | if (Keyboard.isKeyDown(205) || Keyboard.isKeyDown(32)) { // Right, D
42 | vertical++;
43 | }
44 | if ((Keyboard.isKeyDown(57) || Keyboard.isKeyDown(219)) && this.onGround) { // Space, Windows Key
45 | this.motionY = 0.5F;
46 | }
47 |
48 | // Add motion to the player using keyboard input
49 | moveRelative(vertical, forward, this.onGround ? 0.1F : 0.02F);
50 |
51 | // Apply gravity motion
52 | this.motionY -= 0.08D;
53 |
54 | // Move the player using the motion
55 | move(this.motionX, this.motionY, this.motionZ);
56 |
57 | // Decrease motion
58 | this.motionX *= 0.91F;
59 | this.motionY *= 0.98F;
60 | this.motionZ *= 0.91F;
61 |
62 | // Decrease motion on ground
63 | if (this.onGround) {
64 | this.motionX *= 0.7F;
65 | this.motionZ *= 0.7F;
66 | }
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/src/main/java/com/mojang/minecraft/level/DirtyChunkSorter.java:
--------------------------------------------------------------------------------
1 | package com.mojang.minecraft.level;
2 |
3 | import com.mojang.minecraft.Player;
4 |
5 | import java.util.Comparator;
6 |
7 | public class DirtyChunkSorter implements Comparator {
8 |
9 | private final long now = System.currentTimeMillis();
10 |
11 | private final Player player;
12 | private final Frustum frustum;
13 |
14 | /**
15 | * Sort the chunk render order.
16 | * - Chunks that are visible in the camera have a higher priority than chunks behind the camera.
17 | * - Chunks with a higher dirty duration have a higher priority.
18 | * - Chunks closer to the player have a higher priority.
19 | *
20 | * @param player The player for the distance priority.
21 | * @param frustum Frustum for the visible-in-camera priority
22 | */
23 | public DirtyChunkSorter(Player player, Frustum frustum) {
24 | this.player = player;
25 | this.frustum = frustum;
26 | }
27 |
28 | @Override
29 | public int compare(Chunk chunk1, Chunk chunk2) {
30 | // Matching chunk instance
31 | if (chunk1.equals(chunk2))
32 | return 0;
33 |
34 | boolean chunk1Visible = this.frustum.isVisible(chunk1.boundingBox);
35 | boolean chunk2Visible = this.frustum.isVisible(chunk2.boundingBox);
36 |
37 | // Return priority if one of the chunks is not visible
38 | if (chunk1Visible && !chunk2Visible) {
39 | return -1;
40 | }
41 | if (chunk2Visible && !chunk1Visible) {
42 | return 1;
43 | }
44 |
45 | // Get the duration since last chunk dirty
46 | int dirtyDuration1 = (int) ((this.now - chunk1.dirtiedTime) / 2000L);
47 | int dirtyDuration2 = (int) ((this.now - chunk2.dirtiedTime) / 2000L);
48 |
49 | // Return priority if one of the chunks has a bigger dirty duration
50 | if (dirtyDuration1 < dirtyDuration2) {
51 | return -1;
52 | }
53 | if (dirtyDuration1 > dirtyDuration2) {
54 | return 1;
55 | }
56 |
57 | // Decide priority using the distance to the player
58 | return (chunk1.distanceToSqr(this.player) < chunk2.distanceToSqr(this.player)) ? -1 : 1;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/java/com/mojang/minecraft/character/ZombieModel.java:
--------------------------------------------------------------------------------
1 | package com.mojang.minecraft.character;
2 |
3 | public class ZombieModel {
4 |
5 | public Cube head;
6 | public Cube body;
7 |
8 | public Cube rightArm;
9 | public Cube leftArm;
10 |
11 | public Cube rightLeg;
12 | public Cube leftLeg;
13 |
14 | /**
15 | * Create cubes for the zombie model
16 | */
17 | public ZombieModel() {
18 | // Create head cube
19 | this.head = new Cube(0, 0)
20 | .addBox(-4.0F, -8.0F, -4.0F, 8, 8, 8);
21 |
22 | // Create body cube
23 | this.body = new Cube(16, 16)
24 | .addBox(-4.0F, 0.0F, -2.0F, 8, 12, 4);
25 |
26 | // Right arm cube
27 | this.rightArm = new Cube(40, 16)
28 | .addBox(-3.0F, -2.0F, -2.0F, 4, 12, 4);
29 | this.rightArm.setPosition(-5.0F, 2.0F, 0.0F);
30 |
31 | // Left arm cube
32 | this.leftArm = new Cube(40, 16)
33 | .addBox(-1.0F, -2.0F, -2.0F, 4, 12, 4);
34 | this.leftArm.setPosition(5.0F, 2.0F, 0.0F);
35 |
36 | // Right Legs cube
37 | this.rightLeg = new Cube(0, 16)
38 | .addBox(-2.0F, 0.0F, -2.0F, 4, 12, 4);
39 | this.rightLeg.setPosition(-2.0F, 12.0F, 0.0F);
40 |
41 | // Left leg cube
42 | this.leftLeg = new Cube(0, 16)
43 | .addBox(-2.0F, 0.0F, -2.0F, 4, 12, 4);
44 | this.leftLeg.setPosition(2.0F, 12.0F, 0.0F);
45 | }
46 |
47 | /**
48 | * Render the model
49 | *
50 | * @param time Animation offset
51 | */
52 | public void render(double time) {
53 | // Set rotation of cubes
54 | this.head.yRotation = (float) Math.sin(time * 0.83);
55 | this.head.xRotation = (float) Math.sin(time) * 0.8F;
56 | this.rightArm.xRotation = (float) Math.sin(time * 0.6662 + Math.PI) * 2.0F;
57 | this.rightArm.zRotation = (float) (Math.sin(time * 0.2312) + 1.0);
58 | this.leftArm.xRotation = (float) Math.sin(time * 0.6662) * 2.0f;
59 | this.leftArm.zRotation = (float) (Math.sin(time * 0.2812) - 1.0);
60 | this.rightLeg.xRotation = (float) Math.sin(time * 0.6662) * 1.4f;
61 | this.leftLeg.xRotation = (float) Math.sin(time * 0.6662 + Math.PI) * 1.4F;
62 |
63 | // Render cubes
64 | this.head.render();
65 | this.body.render();
66 | this.rightArm.render();
67 | this.leftArm.render();
68 | this.rightLeg.render();
69 | this.leftLeg.render();
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/main/java/com/mojang/minecraft/Timer.java:
--------------------------------------------------------------------------------
1 | package com.mojang.minecraft;
2 |
3 | public class Timer {
4 |
5 | private static final long NS_PER_SECOND = 1000000000L;
6 | private static final long MAX_NS_PER_UPDATE = 1000000000L;
7 | private static final int MAX_TICKS_PER_UPDATE = 100;
8 |
9 | /**
10 | * Amount of ticks per second
11 | */
12 | private final float ticksPerSecond;
13 |
14 | /**
15 | * Last time updated in nano seconds
16 | */
17 | private long lastTime = System.nanoTime();
18 |
19 | /**
20 | * Scale the tick speed
21 | */
22 | public float timeScale = 1.0F;
23 |
24 | /**
25 | * Framerate of the advanceTime update
26 | */
27 | public float fps = 0.0F;
28 |
29 | /**
30 | * Passed time since last game update
31 | */
32 | public float passedTime = 0.0F;
33 |
34 | /**
35 | * The amount of ticks for the current game update.
36 | * It's the passed time as an integer
37 | */
38 | public int ticks;
39 |
40 | /**
41 | * The overflow of the current tick, caused by casting the passed time to an integer
42 | */
43 | public float partialTicks;
44 |
45 | /**
46 | * Timer to control the tick speed independently of the framerate
47 | *
48 | * @param ticksPerSecond Amount of ticks per second
49 | */
50 | public Timer(float ticksPerSecond) {
51 | this.ticksPerSecond = ticksPerSecond;
52 | }
53 |
54 | /**
55 | * This function calculates the amount of ticks required to reach the ticksPerSecond.
56 | * Call this function in the main render loop of the game
57 | */
58 | public void advanceTime() {
59 | long now = System.nanoTime();
60 | long passedNs = now - this.lastTime;
61 |
62 | // Store nano time of this update
63 | this.lastTime = now;
64 |
65 | // Maximum and minimum
66 | passedNs = Math.max(0, passedNs);
67 | passedNs = Math.min(MAX_NS_PER_UPDATE, passedNs);
68 |
69 | // Calculate fps
70 | this.fps = (float) (NS_PER_SECOND / passedNs);
71 |
72 | // Calculate passed time and ticks
73 | this.passedTime += passedNs * this.timeScale * this.ticksPerSecond / NS_PER_SECOND;
74 | this.ticks = (int) this.passedTime;
75 |
76 | // Maximum ticks per update
77 | this.ticks = Math.min(MAX_TICKS_PER_UPDATE, this.ticks);
78 |
79 | // Calculate the overflow of the current tick
80 | this.passedTime -= this.ticks;
81 | this.partialTicks = this.passedTime;
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/main/java/com/mojang/minecraft/MinecraftApplet.java:
--------------------------------------------------------------------------------
1 | package com.mojang.minecraft;
2 |
3 | import java.applet.Applet;
4 | import java.awt.*;
5 |
6 | /**
7 | * Applet to run the game
8 | */
9 | public class MinecraftApplet extends Applet {
10 |
11 | /**
12 | * Applet canvas
13 | */
14 | private Canvas canvas;
15 |
16 | /**
17 | * Game instance
18 | */
19 | private Minecraft minecraft;
20 |
21 | /**
22 | * Game main thread
23 | */
24 | private Thread thread;
25 |
26 | @Override
27 | public void init() {
28 | this.canvas = new Canvas() {
29 | @Override
30 | public void addNotify() {
31 | super.addNotify();
32 |
33 | startGameThread();
34 | }
35 |
36 | @Override
37 | public void removeNotify() {
38 | stopGameThread();
39 |
40 | super.removeNotify();
41 | }
42 | };
43 |
44 | // Create game instance
45 | this.minecraft = new Minecraft(this.canvas, getWidth(), getHeight(), false);
46 | this.minecraft.appletMode = true;
47 |
48 | // Setup canvas
49 | this.setLayout(new BorderLayout());
50 | this.add(this.canvas, "Center");
51 | this.canvas.setFocusable(true);
52 | this.validate();
53 | }
54 |
55 | /**
56 | * Start game loop in new thread
57 | */
58 | public void startGameThread() {
59 | if (this.thread == null) {
60 | (this.thread = new Thread(this.minecraft)).start();
61 | }
62 | }
63 |
64 | @Override
65 | public void start() {
66 |
67 | }
68 |
69 | @Override
70 | public void stop() {
71 |
72 | }
73 |
74 | @Override
75 | public void destroy() {
76 | stopGameThread();
77 | }
78 |
79 | /**
80 | * Stop the game loop and destroy everything
81 | */
82 | public void stopGameThread() {
83 | if (this.thread == null) {
84 | return;
85 | }
86 |
87 | // Stop the game loop
88 | this.minecraft.stop();
89 |
90 | try {
91 | // Wait for 5 seconds
92 | this.thread.join(5000L);
93 | } catch (InterruptedException interruptedException) {
94 |
95 | // Destroy display, mouse and keyboard
96 | try {
97 | this.minecraft.destroy();
98 | } catch (Exception e) {
99 | e.printStackTrace();
100 | }
101 | }
102 |
103 | this.thread = null;
104 | }
105 | }
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/src/main/java/com/mojang/minecraft/particle/ParticleEngine.java:
--------------------------------------------------------------------------------
1 | package com.mojang.minecraft.particle;
2 |
3 | import com.mojang.minecraft.Player;
4 | import com.mojang.minecraft.Textures;
5 | import com.mojang.minecraft.level.Level;
6 | import com.mojang.minecraft.level.Tessellator;
7 |
8 | import java.util.ArrayList;
9 | import java.util.Iterator;
10 | import java.util.List;
11 |
12 | import static org.lwjgl.opengl.GL11.*;
13 |
14 | public class ParticleEngine {
15 |
16 | protected final Level level;
17 |
18 | private final List particles = new ArrayList<>();
19 |
20 | public ParticleEngine(Level level) {
21 | this.level = level;
22 | }
23 |
24 | /**
25 | * Add particle to engine
26 | *
27 | * @param particle The particle to add
28 | */
29 | public void add(Particle particle) {
30 | this.particles.add(particle);
31 | }
32 |
33 | /**
34 | * Tick all particles and remove dead particles
35 | */
36 | public void onTick() {
37 | // Tick all particles
38 | Iterator iterator = this.particles.iterator();
39 | while (iterator.hasNext()) {
40 | Particle particle = iterator.next();
41 |
42 | // Tick this particle
43 | particle.onTick();
44 |
45 | // Remove particle when removed flag is set
46 | if (particle.removed) {
47 | iterator.remove();
48 | }
49 | }
50 | }
51 |
52 | /**
53 | * Render all particles
54 | *
55 | * @param player The player
56 | * @param partialTicks Ticks for interpolation
57 | * @param layer Shadow layer
58 | */
59 | public void render(Player player, Tessellator tessellator, float partialTicks, int layer) {
60 | glEnable(GL_TEXTURE_2D);
61 |
62 | // Bind terrain texture
63 | int id = Textures.loadTexture("/terrain.png", 9728);
64 | glBindTexture(GL_TEXTURE_2D, id);
65 |
66 | // Get camera angel
67 | double cameraX = -Math.cos(Math.toRadians(player.yRotation));
68 | double cameraY = Math.cos(Math.toRadians(player.xRotation));
69 | double cameraZ = -Math.sin(Math.toRadians(player.yRotation));
70 |
71 | // Get additional camera rotation
72 | double cameraXWithY = -cameraZ * Math.sin(Math.toRadians(player.xRotation));
73 | double cameraZWithY = cameraX * Math.sin(Math.toRadians(player.xRotation));
74 |
75 | // Start rendering
76 | glColor4f(0.8F, 0.8F, 0.8F, 1.0F);
77 | tessellator.init();
78 |
79 | // Render all particles in correct layer
80 | for (Particle particle : this.particles) {
81 | if (particle.isLit() ^ layer == 1) {
82 | particle.render(tessellator, partialTicks, (float) cameraX, (float) cameraY, (float) cameraZ, (float) cameraXWithY, (float) cameraZWithY);
83 | }
84 | }
85 |
86 | // Finish rendering
87 | tessellator.flush();
88 | glDisable(GL_TEXTURE_2D);
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/main/java/com/mojang/minecraft/level/tile/Bush.java:
--------------------------------------------------------------------------------
1 | package com.mojang.minecraft.level.tile;
2 |
3 | import com.mojang.minecraft.level.Level;
4 | import com.mojang.minecraft.level.Tessellator;
5 | import com.mojang.minecraft.phys.AABB;
6 |
7 | import java.util.Random;
8 |
9 | public class Bush extends Tile {
10 |
11 | /**
12 | * Create a bush tile with given id
13 | *
14 | * @param id Id of the tile
15 | */
16 | protected Bush(int id) {
17 | super(id);
18 |
19 | // Set texture slot id
20 | this.textureId = 15;
21 | }
22 |
23 | @Override
24 | public void onTick(Level level, int x, int y, int z, Random random) {
25 | int tileIdBelow = level.getTile(x, y - 1, z);
26 |
27 | // Destroy bush if there is no light or no dirt/grass below it
28 | if (!level.isLit(x, y, z) || (tileIdBelow != Tile.dirt.id && tileIdBelow != Tile.grass.id)) {
29 | level.setTile(x, y, z, 0);
30 | }
31 | }
32 |
33 | @Override
34 | public void render(Tessellator tessellator, Level level, int layer, int x, int y, int z) {
35 | // Render in correct layer
36 | if (level.isLit(x, y, z) ^ layer != 1) {
37 | return;
38 | }
39 |
40 | // Texture id
41 | int textureId = this.getTexture(this.textureId);
42 |
43 | // Texture mapping points
44 | float minU = textureId % 16 / 16.0F;
45 | float minV = minU + 999 / 16000.0F;
46 | float maxU = (float) (textureId / 16) / 16.0F;
47 | float maxV = maxU + 999 / 16000.0F;
48 |
49 | // Color
50 | tessellator.color(1.0F, 1.0F, 1.0F);
51 |
52 | // Two sides
53 | for (int i = 0; i < 2; i++) {
54 |
55 | // Rotation
56 | float sin = (float) Math.sin(i * Math.PI / 2 + Math.PI / 4) / 2;
57 | float cos = (float) Math.cos(i * Math.PI / 2 + Math.PI / 4) / 2;
58 |
59 | // Vertex points
60 | float minX = x + 0.5F - sin;
61 | float maxX = x + 0.5F + sin;
62 | float minY = y + 0.0F;
63 | float maxY = y + 1.0F;
64 | float minZ = z + 0.5F - cos;
65 | float maxZ = z + 0.5F + cos;
66 |
67 | // Render bush side
68 | tessellator.vertexUV(minX, maxY, minZ, minV, maxU);
69 | tessellator.vertexUV(maxX, maxY, maxZ, minU, maxU);
70 | tessellator.vertexUV(maxX, minY, maxZ, minU, maxV);
71 | tessellator.vertexUV(minX, minY, minZ, minV, maxV);
72 | tessellator.vertexUV(maxX, maxY, maxZ, minU, maxU);
73 | tessellator.vertexUV(minX, maxY, minZ, minV, maxU);
74 | tessellator.vertexUV(minX, minY, minZ, minV, maxV);
75 | tessellator.vertexUV(maxX, minY, maxZ, minU, maxV);
76 | }
77 | }
78 |
79 | @Override
80 | public AABB getAABB(int x, int y, int z) {
81 | return null;
82 | }
83 |
84 | @Override
85 | public boolean blocksLight() {
86 | return false;
87 | }
88 |
89 | @Override
90 | public boolean isSolid() {
91 | return false;
92 | }
93 |
94 | }
95 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | ### Java template
3 | # Compiled class file
4 | *.class
5 |
6 | # Log file
7 | *.log
8 |
9 | # BlueJ files
10 | *.ctxt
11 |
12 | # Mobile Tools for Java (J2ME)
13 | .mtj.tmp/
14 |
15 | # Package Files #
16 | *.jar
17 | *.war
18 | *.nar
19 | *.ear
20 | *.zip
21 | *.tar.gz
22 | *.rar
23 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
24 | hs_err_pid*
25 |
26 | ### JetBrains template
27 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
28 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
29 |
30 | # User-specific stuff
31 | .idea/**/workspace.xml
32 | .idea/**/tasks.xml
33 | .idea/**/usage.statistics.xml
34 | .idea/**/dictionaries
35 | .idea/**/shelf
36 | .idea/**/misc.xml
37 | .idea/**/discord.xml
38 | .idea/**/encodings.xml
39 | .idea/codeStyles/
40 | .idea/sonarlint/
41 |
42 | # Generated files
43 | .idea/**/contentModel.xml
44 | .idea/**/jarRepositories.xml
45 | .idea/**/uiDesigner.xml
46 | .idea/**/inspectionProfiles
47 | .idea/**/.name
48 | .idea/**/vcs.xml
49 | .idea/**/compiler.xml
50 |
51 | # Sensitive or high-churn files
52 | .idea/**/dataSources/
53 | .idea/**/dataSources.ids
54 | .idea/**/dataSources.local.xml
55 | .idea/**/sqlDataSources.xml
56 | .idea/**/dynamic.xml
57 | .idea/**/dbnavigator.xml
58 | .idea/kotlinScripting.xml
59 |
60 | # Gradle
61 | .idea/**/gradle.xml
62 | .idea/**/libraries
63 |
64 | # Gradle and Maven with auto-import
65 | # When using Gradle or Maven with auto-import, you should exclude module files,
66 | # since they will be recreated, and may cause churn. Uncomment if using
67 | # auto-import.
68 | # .idea/artifacts
69 | # .idea/compiler.xml
70 | .idea/modules.xml
71 | .idea/*.iml
72 | # .idea/modules
73 | # *.iml
74 | # *.ipr
75 |
76 | # CMake
77 | cmake-build-*/
78 |
79 | # Mongo Explorer plugin
80 | .idea/**/mongoSettings.xml
81 |
82 | # File-based project format
83 | *.iws
84 |
85 | # IntelliJ
86 | out/
87 |
88 | # mpeltonen/sbt-idea plugin
89 | .idea_modules/
90 |
91 | # JIRA plugin
92 | atlassian-ide-plugin.xml
93 |
94 | # Cursive Clojure plugin
95 | .idea/replstate.xml
96 |
97 | # Crashlytics plugin (for Android Studio and IntelliJ)
98 | com_crashlytics_export_strings.xml
99 | crashlytics.properties
100 | crashlytics-build.properties
101 |
102 | # Editor-based Rest Client
103 | .idea/httpRequests
104 |
105 | # Android studio 3.1+ serialized cache file
106 | .idea/caches/build_file_checksums.ser
107 |
108 | ### Gradle template
109 | .gradle
110 | /**/build/
111 |
112 | # Ignore Gradle GUI config
113 | gradle-app.setting
114 |
115 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
116 | !gradle-wrapper.jar
117 |
118 | # Cache of project
119 | .gradletasknamecache
120 |
121 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
122 | # gradle/wrapper/gradle-wrapper.properties
123 |
124 | .idea/.gitignore
125 | .idea/modules/
126 | src/main/resources/
127 | src/test/
128 | **/src/main/generated/*
129 |
130 | .idea/intellij-javadocs-4.0.1.xml
131 | run
--------------------------------------------------------------------------------
/src/main/java/com/mojang/minecraft/Textures.java:
--------------------------------------------------------------------------------
1 | package com.mojang.minecraft;
2 |
3 | import org.lwjgl.BufferUtils;
4 |
5 | import javax.imageio.ImageIO;
6 | import java.awt.image.BufferedImage;
7 | import java.io.IOException;
8 | import java.io.InputStream;
9 | import java.nio.ByteBuffer;
10 | import java.util.HashMap;
11 | import java.util.Map;
12 |
13 | import static org.lwjgl.opengl.GL11.*;
14 | import static org.lwjgl.util.glu.GLU.gluBuild2DMipmaps;
15 |
16 | public class Textures {
17 |
18 | private static final Map idMap = new HashMap<>();
19 |
20 | private static int lastId = Integer.MIN_VALUE;
21 |
22 | /**
23 | * Load a texture into OpenGL
24 | *
25 | * @param resourceName Resource path of the image
26 | * @param mode Texture filter mode (GL_NEAREST, GL_LINEAR)
27 | * @return Texture id of OpenGL
28 | */
29 | public static int loadTexture(String resourceName, int mode) {
30 | if (idMap.containsKey(resourceName)) {
31 | return idMap.get(resourceName);
32 | }
33 |
34 | // Generate a new texture id
35 | int id = glGenTextures();
36 |
37 | // Store id in map
38 | idMap.put(resourceName, id);
39 |
40 | // Bind this texture id
41 | bind(id);
42 |
43 | // Set texture filter mode
44 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mode);
45 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mode);
46 |
47 | // Read from resources
48 | InputStream inputStream = Textures.class.getResourceAsStream(resourceName);
49 |
50 | try {
51 | // Read to buffered image
52 | BufferedImage bufferedImage = ImageIO.read(inputStream);
53 |
54 | // Get image size
55 | int width = bufferedImage.getWidth();
56 | int height = bufferedImage.getHeight();
57 |
58 | // Write image pixels into array
59 | int[] pixels = new int[width * height];
60 | bufferedImage.getRGB(0, 0, width, height, pixels, 0, width);
61 |
62 | // Flip RGB order of the integers
63 | for (int i = 0; i < pixels.length; i++) {
64 | int alpha = pixels[i] >> 24 & 0xFF;
65 | int red = pixels[i] >> 16 & 0xFF;
66 | int green = pixels[i] >> 8 & 0xFF;
67 | int blue = pixels[i] & 0xFF;
68 |
69 | // ARGB to ABGR
70 | pixels[i] = alpha << 24 | blue << 16 | green << 8 | red;
71 | }
72 |
73 | // Create bytebuffer from pixel array
74 | ByteBuffer byteBuffer = BufferUtils.createByteBuffer(width * height * 4);
75 | byteBuffer.asIntBuffer().put(pixels);
76 |
77 | // Write texture to opengl
78 | gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, width, height, GL_RGBA, GL_UNSIGNED_BYTE, byteBuffer);
79 | } catch (IOException exception) {
80 | throw new RuntimeException("Could not load texture " + resourceName, exception);
81 | }
82 |
83 | return id;
84 | }
85 |
86 | /**
87 | * Bind the texture to OpenGL using the id from {@link #loadTexture(String, int)}
88 | *
89 | * @param id Texture id
90 | */
91 | public static void bind(int id) {
92 | if (id != lastId) {
93 | glBindTexture(GL_TEXTURE_2D, id);
94 | lastId = id;
95 | }
96 | }
97 | }
--------------------------------------------------------------------------------
/src/main/java/com/mojang/minecraft/character/Zombie.java:
--------------------------------------------------------------------------------
1 | package com.mojang.minecraft.character;
2 |
3 | import com.mojang.minecraft.Entity;
4 | import com.mojang.minecraft.Textures;
5 | import com.mojang.minecraft.level.Level;
6 |
7 | import static org.lwjgl.opengl.GL11.*;
8 |
9 | public class Zombie extends Entity {
10 |
11 | private final ZombieModel model = new ZombieModel();
12 |
13 | public double rotation = Math.random() * Math.PI * 2;
14 | public double rotationMotionFactor = (Math.random() + 1.0) * 0.01F;
15 |
16 | public float timeOffset = (float) (Math.random() * 1239813.0F);
17 | public float speed = 1.0F;
18 |
19 | /**
20 | * Human model test
21 | *
22 | * @param level Level of the zombie
23 | */
24 | public Zombie(Level level, double x, double y, double z) {
25 | super(level);
26 |
27 | // Set the position of the entity
28 | setPosition(x, y, z);
29 | }
30 |
31 |
32 | @Override
33 | public void onTick() {
34 | super.onTick();
35 |
36 | // Kill in void
37 | if (this.y < -100.0F) {
38 | remove();
39 | }
40 |
41 | // Increase movement direction
42 | this.rotation += this.rotationMotionFactor;
43 |
44 | // Modify direction motion factor
45 | this.rotationMotionFactor *= 0.99D;
46 | this.rotationMotionFactor += (Math.random() - Math.random()) * Math.random() * Math.random() * 0.009999999776482582;
47 |
48 | // Calculate movement input using rotation
49 | float vertical = (float) Math.sin(this.rotation);
50 | float forward = (float) Math.cos(this.rotation);
51 |
52 | // Randomly jump
53 | if (this.onGround && Math.random() < 0.08F) {
54 | this.motionY = 0.5F;
55 | }
56 |
57 | // Apply motion the zombie using the vertical and forward direction
58 | moveRelative(vertical, forward, this.onGround ? 0.1F : 0.02F);
59 |
60 | // Apply gravity
61 | this.motionY -= 0.08F;
62 |
63 | // Move the entity using motion
64 | move(this.motionX, this.motionY, this.motionZ);
65 |
66 | // Decrease motion speed
67 | this.motionX *= 0.91F;
68 | this.motionY *= 0.98F;
69 | this.motionZ *= 0.91F;
70 |
71 | // Decrease motion speed on ground
72 | if (this.onGround) {
73 | this.motionX *= 0.7F;
74 | this.motionZ *= 0.7F;
75 | }
76 | }
77 |
78 | /**
79 | * Render the zombie
80 | *
81 | * @param partialTicks Overflow for interpolation
82 | */
83 | public void render(float partialTicks) {
84 | // Start rendering
85 | glPushMatrix();
86 | glEnable(GL_TEXTURE_2D);
87 |
88 | // Bind texture
89 | glBindTexture(GL_TEXTURE_2D, Textures.loadTexture("/char.png", GL_NEAREST));
90 |
91 | // Zombie animation time
92 | double time = System.nanoTime() / 1000000000D * 10.0 * this.speed + this.timeOffset;
93 |
94 | // Interpolate entity position
95 | double interpolatedX = this.prevX + (this.x - this.prevX) * partialTicks;
96 | double interpolatedY = this.prevY + (this.y - this.prevY) * partialTicks;
97 | double interpolatedZ = this.prevZ + (this.z - this.prevZ) * partialTicks;
98 |
99 | // Translate using interpolated position
100 | glTranslated(interpolatedX, interpolatedY, interpolatedZ);
101 |
102 | // Flip the entity because it's upside down
103 | glScalef(1.0F, -1.0F, 1.0F);
104 |
105 | // Actual size of the entity
106 | float size = 7.0F / 120.0F;
107 | glScalef(size, size, size);
108 |
109 | // Body offset animation
110 | double offsetY = Math.abs(Math.sin(time * 2.0D / 3.0D)) * 5.0 + 23.0D;
111 | glTranslated(0.0F, -offsetY, 0.0F);
112 |
113 | // Rotate the entity
114 | glRotated(Math.toDegrees(this.rotation) + 180, 0.0F, 1.0F, 0.0F);
115 |
116 | // Render the model
117 | this.model.render(time);
118 |
119 | // Stop rendering
120 | glDisable(GL_TEXTURE_2D);
121 | glPopMatrix();
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/src/main/java/com/mojang/minecraft/level/PerlinNoiseFilter.java:
--------------------------------------------------------------------------------
1 | package com.mojang.minecraft.level;
2 |
3 | import java.util.Random;
4 |
5 | public class PerlinNoiseFilter {
6 |
7 | private static final int FUZZINESS = 16;
8 | private final int octave;
9 |
10 | /**
11 | * Perlin noise generator
12 | *
13 | * @param octave The strength of the noise
14 | */
15 | public PerlinNoiseFilter(int octave) {
16 | this.octave = octave;
17 | }
18 |
19 | /**
20 | * Read random noise values with given amount
21 | *
22 | * @param width Noise width
23 | * @param height Noise height
24 | * @return Noise map
25 | */
26 | public int[] read(int width, int height) {
27 | Random random = new Random();
28 |
29 | int[] table = new int[width * height];
30 |
31 | // Generate temporary table array with random values between -128 and +128 and multiplied with the fuzziness
32 | for (int step = width >> this.octave, y = 0; y < height; y += step) {
33 | for (int x = 0; x < width; x += step) {
34 | table[x + y * width] = (random.nextInt(256) - 128) * FUZZINESS;
35 | }
36 | }
37 |
38 | // Mutate values in table
39 | for (int step = width >> this.octave; step > 1; step /= 2) {
40 | int max = 256 * (step << this.octave);
41 | int halfStep = step / 2;
42 |
43 | // First mutation
44 | for (int y = 0; y < height; y += step) {
45 | for (int x = 0; x < width; x += step) {
46 | // Get value at index
47 | int value = table[x % width + y % height * width];
48 |
49 | // Add step to create index for value
50 | int stepValueX = table[(x + step) % width + y % height * width];
51 | int stepValueY = table[x % width + (y + step) % height * width];
52 | int stepValueXY = table[(x + step) % width + (y + step) % height * width];
53 |
54 | // Combine values for new value
55 | int mutatedValue = (value + stepValueY + stepValueX + stepValueXY) / 4 + random.nextInt(max * 2) - max;
56 |
57 | // Update value in table
58 | table[x + halfStep + (y + halfStep) * width] = mutatedValue;
59 | }
60 | }
61 |
62 | // Second mutation
63 | for (int y = 0; y < height; y += step) {
64 | for (int x = 0; x < width; x += step) {
65 | // Get value at index
66 | int value = table[x + y * width];
67 |
68 | // Add step to create index for value
69 | int stepValueX = table[(x + step) % width + y * width];
70 | int stepValueY = table[x + (y + step) % width * width];
71 |
72 | // Add step and half step to create index for value
73 | int halfStepValueXPos = table[(x + halfStep & width - 1) + (y + halfStep - step & height - 1) * width];
74 | int halfStepValueYPos = table[(x + halfStep - step & width - 1) + (y + halfStep & height - 1) * width];
75 |
76 | // Add half step to create index for value
77 | int halfStepValue = table[(x + halfStep) % width + (y + halfStep) % height * width];
78 |
79 | // Combine values for new value
80 | int mutatedValueX = (value + stepValueX + halfStepValue + halfStepValueXPos) / 4 + random.nextInt(max * 2) - max;
81 | int mutatedValueY = (value + stepValueY + halfStepValue + halfStepValueYPos) / 4 + random.nextInt(max * 2) - max;
82 |
83 | // Update values in table
84 | table[x + halfStep + y * width] = mutatedValueX;
85 | table[x + (y + halfStep) * width] = mutatedValueY;
86 | }
87 | }
88 | }
89 |
90 | // Create result array
91 | int[] result = new int[width * height];
92 |
93 | // Generate output values
94 | for (int y = 0; y < height; y++) {
95 | for (int x = 0; x < width; x++) {
96 | result[x + y * width] = table[x % width + y % height * width] / 512 + 128;
97 | }
98 | }
99 |
100 | return result;
101 | }
102 |
103 | }
104 |
--------------------------------------------------------------------------------
/src/main/java/com/mojang/minecraft/level/Tessellator.java:
--------------------------------------------------------------------------------
1 | package com.mojang.minecraft.level;
2 |
3 | import org.lwjgl.BufferUtils;
4 |
5 | import java.nio.FloatBuffer;
6 |
7 | import static org.lwjgl.opengl.GL11.*;
8 |
9 | public class Tessellator {
10 |
11 | private static final int MAX_VERTICES = 100000;
12 |
13 | private final FloatBuffer vertexBuffer = BufferUtils.createFloatBuffer(MAX_VERTICES * 3);
14 | private final FloatBuffer textureCoordinateBuffer = BufferUtils.createFloatBuffer(MAX_VERTICES * 2);
15 | private final FloatBuffer colorBuffer = BufferUtils.createFloatBuffer(MAX_VERTICES * 3);
16 |
17 | private int vertices = 0;
18 |
19 | // Texture
20 | private boolean hasTexture = false;
21 | private float textureU;
22 | private float textureV;
23 |
24 | // Color
25 | private boolean hasColor;
26 | private float red;
27 | private float green;
28 | private float blue;
29 |
30 | /**
31 | * Reset the buffer
32 | */
33 | public void init() {
34 | clear();
35 | }
36 |
37 | /**
38 | * Add a vertex point to buffer
39 | *
40 | * @param x Vertex point x
41 | * @param y Vertex point y
42 | * @param z Vertex point z
43 | */
44 | public void vertex(float x, float y, float z) {
45 | // Vertex
46 | this.vertexBuffer.put(this.vertices * 3, x);
47 | this.vertexBuffer.put(this.vertices * 3 + 1, y);
48 | this.vertexBuffer.put(this.vertices * 3 + 2, z);
49 |
50 | // Texture coordinate
51 | if (this.hasTexture) {
52 | this.textureCoordinateBuffer.put(this.vertices * 2, this.textureU);
53 | this.textureCoordinateBuffer.put(this.vertices * 2 + 1, this.textureV);
54 | }
55 |
56 | // Color coordinate
57 | if (this.hasColor) {
58 | this.colorBuffer.put(this.vertices * 3, this.red);
59 | this.colorBuffer.put(this.vertices * 3 + 1, this.green);
60 | this.colorBuffer.put(this.vertices * 3 + 2, this.blue);
61 | }
62 |
63 | this.vertices++;
64 |
65 | // Flush if there are too many vertices in the buffer
66 | if (this.vertices == MAX_VERTICES) {
67 | flush();
68 | }
69 | }
70 |
71 | /**
72 | * Add a vertex and set the texture UV mappings
73 | *
74 | * @param x Vertex point x
75 | * @param y Vertex point y
76 | * @param z Vertex point z
77 | * @param textureU Texture U point
78 | * @param textureV Texture V point
79 | */
80 | public void vertexUV(float x, float y, float z, float textureU, float textureV) {
81 | texture(textureU, textureV);
82 | vertex(x, y, z);
83 | }
84 |
85 | /**
86 | * Set texture UV mappings
87 | *
88 | * @param textureU Texture U point
89 | * @param textureV Texture V point
90 | */
91 | public void texture(float textureU, float textureV) {
92 | this.hasTexture = true;
93 | this.textureU = textureU;
94 | this.textureV = textureV;
95 | }
96 |
97 | /**
98 | * Set the RGB color
99 | *
100 | * @param red Red (0.0 - 1.0)
101 | * @param green Green (0.0 - 1.0)
102 | * @param blue Blue (0.0 - 1.0)
103 | */
104 | public void color(float red, float green, float blue) {
105 | this.hasColor = true;
106 | this.red = red;
107 | this.green = green;
108 | this.blue = blue;
109 | }
110 |
111 | /**
112 | * Render the buffer
113 | */
114 | public void flush() {
115 | this.vertexBuffer.flip();
116 | this.textureCoordinateBuffer.flip();
117 |
118 | // Set points
119 | glVertexPointer(3, GL_POINTS, this.vertexBuffer);
120 | if (this.hasTexture) {
121 | glTexCoordPointer(2, GL_POINTS, this.textureCoordinateBuffer);
122 | }
123 | if (this.hasColor) {
124 | glColorPointer(3, GL_POINTS, this.colorBuffer);
125 | }
126 |
127 | // Enable client states
128 | glEnableClientState(GL_VERTEX_ARRAY);
129 | if (this.hasTexture) {
130 | glEnableClientState(GL_TEXTURE_COORD_ARRAY);
131 | }
132 | if (this.hasColor) {
133 | glEnableClientState(GL_COLOR_ARRAY);
134 | }
135 |
136 | // Draw quads
137 | glDrawArrays(GL_QUADS, GL_POINTS, this.vertices);
138 |
139 | // Reset after rendering
140 | glDisableClientState(GL_VERTEX_ARRAY);
141 | if (this.hasTexture) {
142 | glDisableClientState(GL_TEXTURE_COORD_ARRAY);
143 | }
144 | if (this.hasColor) {
145 | glDisableClientState(GL_COLOR_ARRAY);
146 | }
147 | clear();
148 | }
149 |
150 | /**
151 | * Reset vertex buffer
152 | */
153 | private void clear() {
154 | this.vertexBuffer.clear();
155 | this.textureCoordinateBuffer.clear();
156 | this.vertices = 0;
157 |
158 | this.hasTexture = false;
159 | this.hasColor = false;
160 | }
161 | }
--------------------------------------------------------------------------------
/src/main/java/com/mojang/minecraft/particle/Particle.java:
--------------------------------------------------------------------------------
1 | package com.mojang.minecraft.particle;
2 |
3 | import com.mojang.minecraft.Entity;
4 | import com.mojang.minecraft.level.Level;
5 | import com.mojang.minecraft.level.Tessellator;
6 |
7 | public class Particle extends Entity {
8 |
9 | public int textureId;
10 |
11 | private final float textureUOffset;
12 | private final float textureVOffset;
13 |
14 | private final float size;
15 | private final int lifetime;
16 |
17 | private int age = 0;
18 |
19 | /**
20 | * Particle entity
21 | *
22 | * @param level The level
23 | * @param x Particle location x
24 | * @param y Particle location y
25 | * @param z Particle location z
26 | * @param motionX Particle motion x
27 | * @param motionY Particle motion y
28 | * @param motionZ Particle motion z
29 | * @param textureId Texture slot id of the particle
30 | */
31 | public Particle(Level level, double x, double y, double z, double motionX, double motionY, double motionZ, int textureId) {
32 | super(level);
33 |
34 | // Set texture
35 | this.textureId = textureId;
36 |
37 | // Set size of the particle
38 | setSize(0.2F, 0.2F);
39 | this.heightOffset = this.boundingBoxHeight / 2.0F;
40 |
41 | // Set position
42 | setPosition(x, y, z);
43 |
44 | // Set motion and add random values
45 | this.motionX = motionX + (Math.random() * 2.0D - 1.0D) * 0.4D;
46 | this.motionY = motionY + (Math.random() * 2.0D - 1.0D) * 0.4D;
47 | this.motionZ = motionZ + (Math.random() * 2.0D - 1.0D) * 0.4D;
48 |
49 | // Create random speed
50 | double speed = (Math.random() + Math.random() + 1.0D) * 0.15D;
51 |
52 | // Apply speed
53 | double distance = Math.sqrt(this.motionX * this.motionX + this.motionY * this.motionY + this.motionZ * this.motionZ);
54 | this.motionX = this.motionX / distance * speed * 0.7D;
55 | this.motionY = this.motionY / distance * speed;
56 | this.motionZ = this.motionZ / distance * speed * 0.7D;
57 |
58 | // Create random texture offset
59 | this.textureUOffset = (float) Math.random() * 3.0F;
60 | this.textureVOffset = (float) Math.random() * 3.0F;
61 |
62 | this.size = (float) (Math.random() * 0.5D + 0.5D);
63 | this.lifetime = (int) (4.0D / (Math.random() * 0.9D + 0.1D));
64 | }
65 |
66 | @Override
67 | public void onTick() {
68 | super.onTick();
69 |
70 | // Kill randomly
71 | if (this.age++ >= this.lifetime) {
72 | remove();
73 | }
74 |
75 | // Apply gravity
76 | this.motionY -= 0.06D;
77 |
78 | // Move the particle using motion
79 | this.move(this.motionX, this.motionY, this.motionZ);
80 |
81 | // Decrease motion speed
82 | this.motionX *= 0.98D;
83 | this.motionY *= 0.98D;
84 | this.motionZ *= 0.98D;
85 |
86 | // Decrease motion speed on ground
87 | if (this.onGround) {
88 | this.motionX *= 0.7D;
89 | this.motionZ *= 0.7D;
90 | }
91 | }
92 |
93 | /**
94 | * Render particle
95 | *
96 | * @param tessellator Tessellator for rendering
97 | * @param partialTicks Ticks for interpolation
98 | * @param cameraX Camera rotation X
99 | * @param cameraY Camera rotation Y
100 | * @param cameraZ Camera rotation Z
101 | * @param cameraXWithY Additional camera rotation x including the y rotation
102 | * @param cameraZWithY Additional camera rotation z including the y rotation
103 | */
104 | public void render(Tessellator tessellator, float partialTicks, float cameraX, float cameraY, float cameraZ, float cameraXWithY, float cameraZWithY) {
105 | // UV mapping points
106 | float minU = (this.textureId % 16 + this.textureUOffset / 4.0F) / 16.0F;
107 | float maxU = minU + 999.0F / 64000.0F;
108 | float minV = ((float) (this.textureId / 16) + this.textureVOffset / 4.0F) / 16.0F;
109 | float maxV = minV + 999.0F / 64000.0F;
110 |
111 | // Interpolate position
112 | float x = (float) (this.prevX + (this.x - this.prevX) * partialTicks);
113 | float y = (float) (this.prevY + (this.y - this.prevY) * partialTicks);
114 | float z = (float) (this.prevZ + (this.z - this.prevZ) * partialTicks);
115 |
116 | // Size of the particle
117 | float size = this.size * 0.1F;
118 |
119 | // Render vertices
120 | tessellator.vertexUV(x - cameraX * size - cameraXWithY * size, y - cameraY * size, z - cameraZ * size - cameraZWithY * size, minU, maxV);
121 | tessellator.vertexUV(x - cameraX * size + cameraXWithY * size, y + cameraY * size, z - cameraZ * size + cameraZWithY * size, minU, minV);
122 | tessellator.vertexUV(x + cameraX * size + cameraXWithY * size, y + cameraY * size, z + cameraZ * size + cameraZWithY * size, maxU, minV);
123 | tessellator.vertexUV(x + cameraX * size - cameraXWithY * size, y - cameraY * size, z + cameraZ * size - cameraZWithY * size, maxU, maxV);
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/src/main/java/com/mojang/minecraft/level/Chunk.java:
--------------------------------------------------------------------------------
1 | package com.mojang.minecraft.level;
2 |
3 | import com.mojang.minecraft.Player;
4 | import com.mojang.minecraft.level.tile.Tile;
5 | import com.mojang.minecraft.phys.AABB;
6 |
7 | import static org.lwjgl.opengl.GL11.*;
8 |
9 |
10 | public class Chunk {
11 |
12 | private static final Tessellator TESSELLATOR = new Tessellator();
13 |
14 | /**
15 | * Global rebuild statistic
16 | */
17 | public static int updates;
18 | public long dirtiedTime;
19 |
20 | /**
21 | * Internal rebuild statistic
22 | */
23 | private static long totalTime;
24 | private static int totalUpdates;
25 |
26 | /**
27 | * The game level
28 | */
29 | private final Level level;
30 |
31 | /**
32 | * Bounding box values
33 | */
34 | public AABB boundingBox;
35 | private final int minX, minY, minZ;
36 | private final int maxX, maxY, maxZ;
37 | private final float x, y, z;
38 |
39 | /**
40 | * Rendering states
41 | */
42 | private final int lists;
43 | private boolean dirty = true;
44 |
45 | /**
46 | * Chunk containing a part of the tiles in a level
47 | *
48 | * @param level The game level
49 | * @param minX Minimal location X
50 | * @param minY Minimal location Y
51 | * @param minZ Minimal location Z
52 | * @param maxX Maximal location X
53 | * @param maxY Maximal location Y
54 | * @param maxZ Maximal location Z
55 | */
56 | public Chunk(Level level, int minX, int minY, int minZ, int maxX, int maxY, int maxZ) {
57 | this.level = level;
58 |
59 | this.minX = minX;
60 | this.minY = minY;
61 | this.minZ = minZ;
62 | this.maxX = maxX;
63 | this.maxY = maxY;
64 | this.maxZ = maxZ;
65 |
66 | // Center of chunk
67 | this.x = (minX + maxX) / 2.0f;
68 | this.y = (minY + maxY) / 2.0f;
69 | this.z = (minZ + maxZ) / 2.0f;
70 |
71 | // Generate lists id
72 | this.lists = glGenLists(2);
73 |
74 | // Create bounding box object of chunk
75 | this.boundingBox = new AABB(minX, minY, minZ, maxX, maxY, maxZ);
76 | }
77 |
78 | /**
79 | * Render all tiles in this chunk
80 | *
81 | * @param layer The layer of the chunk (For shadows)
82 | */
83 | public void rebuild(int layer) {
84 | // Update global stats
85 | updates++;
86 |
87 | // Mark chunk as no longer dirty
88 | this.dirty = false;
89 |
90 | // Tile render counter
91 | int tiles = 0;
92 | long timeRebuildStart = System.nanoTime();
93 |
94 | // Setup tile rendering
95 | glNewList(this.lists + layer, GL_COMPILE);
96 | TESSELLATOR.init();
97 |
98 | // For each tile in this chunk
99 | for (int x = this.minX; x < this.maxX; ++x) {
100 | for (int y = this.minY; y < this.maxY; ++y) {
101 | for (int z = this.minZ; z < this.maxZ; ++z) {
102 | int tileId = this.level.getTile(x, y, z);
103 |
104 | // Is a tile at this location?
105 | if (tileId > 0) {
106 | // Render the tile
107 | Tile.tiles[tileId].render(TESSELLATOR, this.level, layer, x, y, z);
108 |
109 | // Increase tile render counter
110 | tiles++;
111 | }
112 | }
113 | }
114 | }
115 |
116 | // Finish tile rendering
117 | TESSELLATOR.flush();
118 | glEndList();
119 |
120 | // Update chunk update counter
121 | if (tiles > 0) {
122 | totalTime += System.nanoTime() - timeRebuildStart;
123 | totalUpdates++;
124 | }
125 | }
126 |
127 | /**
128 | * Rebuild the chunk for all layers
129 | */
130 | public void rebuild() {
131 | rebuild(0);
132 | rebuild(1);
133 | }
134 |
135 | /**
136 | * Render all tiles in this chunk
137 | *
138 | * @param layer The render layer (Shadow layer)
139 | */
140 | public void render(int layer) {
141 | // Call lists id to render the chunk
142 | glCallList(this.lists + layer);
143 | }
144 |
145 | /**
146 | * Mark chunk as dirty. The chunk will rebuild in the next frame
147 | */
148 | public void setDirty() {
149 | if (!this.dirty) {
150 | this.dirtiedTime = System.currentTimeMillis();
151 | }
152 |
153 | this.dirty = true;
154 | }
155 |
156 | /**
157 | * State of the chunk for rebuild
158 | *
159 | * @return Chunk is dirty
160 | */
161 | public boolean isDirty() {
162 | return dirty;
163 | }
164 |
165 | /**
166 | * Calculate squared distance to the player
167 | *
168 | * @param player The player for the location
169 | * @return The squared distance from the center of the chunk to the player
170 | */
171 | public double distanceToSqr(Player player) {
172 | double distanceX = player.x - this.x;
173 | double distanceY = player.y - this.y;
174 | double distanceZ = player.z - this.z;
175 | return distanceX * distanceX + distanceY * distanceY + distanceZ * distanceZ;
176 | }
177 | }
178 |
--------------------------------------------------------------------------------
/src/main/java/com/mojang/minecraft/Entity.java:
--------------------------------------------------------------------------------
1 | package com.mojang.minecraft;
2 |
3 | import com.mojang.minecraft.level.Level;
4 | import com.mojang.minecraft.phys.AABB;
5 |
6 | import java.util.List;
7 |
8 | public abstract class Entity {
9 |
10 | private final Level level;
11 |
12 | public double x, y, z;
13 | public double prevX, prevY, prevZ;
14 | public double motionX, motionY, motionZ;
15 | public float xRotation, yRotation;
16 |
17 | public AABB boundingBox;
18 | protected float boundingBoxWidth = 0.6F;
19 | protected float boundingBoxHeight = 1.8F;
20 |
21 | protected boolean onGround;
22 | protected float heightOffset;
23 |
24 | public boolean removed;
25 |
26 | /**
27 | * Entity with physics
28 | *
29 | * @param level Level of the entity
30 | */
31 | public Entity(Level level) {
32 | this.level = level;
33 |
34 | resetPosition();
35 | }
36 |
37 | /**
38 | * Set the entity to a specific location
39 | *
40 | * @param x Position x
41 | * @param y Position y
42 | * @param z Position z
43 | */
44 | public void setPosition(double x, double y, double z) {
45 | // Set the position of the entity
46 | this.x = x;
47 | this.y = y;
48 | this.z = z;
49 |
50 | // Entity size
51 | float width = this.boundingBoxWidth / 2.0F;
52 | float height = this.boundingBoxHeight / 2.0F;
53 |
54 | // Set the position of the bounding box
55 | this.boundingBox = new AABB(x - width, y - height,
56 | z - width, x + width,
57 | y + height, z + width);
58 | }
59 |
60 | /**
61 | * Reset the position of the entity to a random location on the level
62 | */
63 | protected void resetPosition() {
64 | float x = (float) Math.random() * this.level.width;
65 | float y = (float) (this.level.depth + 3);
66 | float z = (float) Math.random() * this.level.height;
67 |
68 | setPosition(x, y, z);
69 | }
70 |
71 | /**
72 | * Remove the entity in the next tick
73 | */
74 | public void remove() {
75 | this.removed = true;
76 | }
77 |
78 | /**
79 | * Set the size of the bounding box
80 | *
81 | * @param width Width of the bounding box
82 | * @param height Height of the bounding box
83 | */
84 | protected void setSize(float width, float height) {
85 | this.boundingBoxWidth = width;
86 | this.boundingBoxHeight = height;
87 | }
88 |
89 | /**
90 | * Turn the head using motion yaw and pitch
91 | *
92 | * @param x Rotate the head using yaw
93 | * @param y Rotate the head using pitch
94 | */
95 | public void turn(float x, float y) {
96 | this.yRotation += x * 0.15F;
97 | this.xRotation -= y * 0.15F;
98 |
99 | // Pitch limit
100 | this.xRotation = Math.max(-90.0F, this.xRotation);
101 | this.xRotation = Math.min(90.0F, this.xRotation);
102 | }
103 |
104 | /**
105 | * Update the entity
106 | */
107 | public void onTick() {
108 | // Store previous position
109 | this.prevX = this.x;
110 | this.prevY = this.y;
111 | this.prevZ = this.z;
112 | }
113 |
114 | /**
115 | * Move entity relative in level with collision check
116 | *
117 | * @param x Relative x
118 | * @param y Relative y
119 | * @param z Relative z
120 | */
121 | public void move(double x, double y, double z) {
122 | double prevX = x;
123 | double prevY = y;
124 | double prevZ = z;
125 |
126 | // Get surrounded tiles
127 | List aABBs = this.level.getCubes(this.boundingBox.expand(x, y, z));
128 |
129 | // Check for Y collision
130 | for (AABB abb : aABBs) {
131 | y = abb.clipYCollide(this.boundingBox, y);
132 | }
133 | this.boundingBox.move(0.0F, y, 0.0F);
134 |
135 | // Check for X collision
136 | for (AABB aABB : aABBs) {
137 | x = aABB.clipXCollide(this.boundingBox, x);
138 | }
139 | this.boundingBox.move(x, 0.0F, 0.0F);
140 |
141 | // Check for Z collision
142 | for (AABB aABB : aABBs) {
143 | z = aABB.clipZCollide(this.boundingBox, z);
144 | }
145 | this.boundingBox.move(0.0F, 0.0F, z);
146 |
147 | // Update on ground state
148 | this.onGround = prevY != y && prevY < 0.0F;
149 |
150 | // Stop motion on collision
151 | if (prevX != x) this.motionX = 0.0D;
152 | if (prevY != y) this.motionY = 0.0D;
153 | if (prevZ != z) this.motionZ = 0.0D;
154 |
155 | // Move the actual entity position
156 | this.x = (this.boundingBox.minX + this.boundingBox.maxX) / 2.0D;
157 | this.y = this.boundingBox.minY + this.heightOffset;
158 | this.z = (this.boundingBox.minZ + this.boundingBox.maxZ) / 2.0D;
159 | }
160 |
161 |
162 | /**
163 | * Add motion to the entity in the facing direction with given speed
164 | *
165 | * @param x Motion to add on X axis
166 | * @param z Motion to add on Z axis
167 | * @param speed Strength of the added motion
168 | */
169 | protected void moveRelative(float x, float z, float speed) {
170 | float distance = x * x + z * z;
171 |
172 | // Stop moving if too slow
173 | if (distance < 0.01F)
174 | return;
175 |
176 | // Apply speed to relative movement
177 | distance = speed / (float) Math.sqrt(distance);
178 | x *= distance;
179 | z *= distance;
180 |
181 | // Calculate sin and cos of entity rotation
182 | double sin = Math.sin(Math.toRadians(this.yRotation));
183 | double cos = Math.cos(Math.toRadians(this.yRotation));
184 |
185 | // Move the entity in facing direction
186 | this.motionX += x * cos - z * sin;
187 | this.motionZ += z * cos + x * sin;
188 | }
189 |
190 | /**
191 | * Is entity in sun
192 | *
193 | * @return Entity is in sunlight
194 | */
195 | public boolean isLit() {
196 | return this.level.isLit((int) this.x, (int) this.y, (int) this.z);
197 | }
198 | }
199 |
--------------------------------------------------------------------------------
/src/main/java/com/mojang/minecraft/character/Cube.java:
--------------------------------------------------------------------------------
1 | package com.mojang.minecraft.character;
2 |
3 | import static org.lwjgl.opengl.GL11.*;
4 |
5 | public class Cube {
6 |
7 | private Polygon[] polygons;
8 |
9 | private int textureOffsetX;
10 | private int textureOffsetY;
11 |
12 | public float x;
13 | public float y;
14 | public float z;
15 |
16 | public float xRotation;
17 | public float yRotation;
18 | public float zRotation;
19 |
20 | /**
21 | * Create cube object
22 | *
23 | * @param textureOffsetX x offset position on the texture
24 | * @param textureOffsetY y offset position on the texture
25 | */
26 | public Cube(int textureOffsetX, int textureOffsetY) {
27 | this.textureOffsetX = textureOffsetX;
28 | this.textureOffsetY = textureOffsetY;
29 | }
30 |
31 | /**
32 | * Set the texture offset position of the cube
33 | *
34 | * @param textureOffsetX Offset position x
35 | * @param textureOffsetY Offset position y
36 | */
37 | public void setTextureOffset(int textureOffsetX, int textureOffsetY) {
38 | this.textureOffsetX = textureOffsetX;
39 | this.textureOffsetY = textureOffsetY;
40 | }
41 |
42 | /**
43 | * Create box using offset position and width, height and depth
44 | *
45 | * @param offsetX X offset of the render position
46 | * @param offsetY Y offset of the render position
47 | * @param offsetZ Z offset of the render position
48 | * @param width Cube width
49 | * @param height Cube height
50 | * @param depth Cube depth
51 | */
52 | public Cube addBox(float offsetX, float offsetY, float offsetZ, int width, int height, int depth) {
53 | this.polygons = new Polygon[6];
54 |
55 | float x = offsetX + width;
56 | float y = offsetY + height;
57 | float z = offsetZ + depth;
58 |
59 | // Create bottom vertex points of cube
60 | Vertex vertexBottom1 = new Vertex(offsetX, offsetY, offsetZ, 0.0F, 0.0F);
61 | Vertex vertexBottom2 = new Vertex(x, offsetY, offsetZ, 0.0F, 8.0F);
62 | Vertex vertexBottom3 = new Vertex(offsetX, offsetY, z, 0.0F, 0.0F);
63 | Vertex vertexBottom4 = new Vertex(x, offsetY, z, 0.0F, 8.0F);
64 |
65 | // Create top vertex points of cube
66 | Vertex vertexTop1 = new Vertex(x, y, z, 8.0F, 8.0F);
67 | Vertex vertexTop2 = new Vertex(offsetX, y, z, 8.0F, 0.0F);
68 | Vertex vertexTop3 = new Vertex(x, y, offsetZ, 8.0F, 8.0F);
69 | Vertex vertexTop4 = new Vertex(offsetX, y, offsetZ, 8.0F, 0.0F);
70 |
71 | // Create polygons for each cube side
72 | this.polygons[0] = new Polygon(
73 | new Vertex[]{
74 | vertexBottom4, vertexBottom2, vertexTop3, vertexTop1
75 | },
76 | this.textureOffsetX + depth + width,
77 | this.textureOffsetY + depth,
78 | this.textureOffsetX + depth + width + depth,
79 | this.textureOffsetY + depth + height
80 | );
81 |
82 | this.polygons[1] = new Polygon(
83 | new Vertex[]{
84 | vertexBottom1, vertexBottom3, vertexTop2, vertexTop4
85 | },
86 | this.textureOffsetX,
87 | this.textureOffsetY + depth,
88 | this.textureOffsetX + depth,
89 | this.textureOffsetY + depth + height
90 | );
91 |
92 | this.polygons[2] = new Polygon(
93 | new Vertex[]{
94 | vertexBottom4, vertexBottom3, vertexBottom1, vertexBottom2
95 | },
96 | this.textureOffsetX + depth,
97 | this.textureOffsetY,
98 | this.textureOffsetX + depth + width,
99 | this.textureOffsetY + depth
100 | );
101 |
102 | this.polygons[3] = new Polygon(
103 | new Vertex[]{
104 | vertexTop3, vertexTop4, vertexTop2, vertexTop1
105 | },
106 | this.textureOffsetX + depth + width,
107 | this.textureOffsetY,
108 | this.textureOffsetX + depth + width + width,
109 | this.textureOffsetY + depth
110 | );
111 |
112 | this.polygons[4] = new Polygon(
113 | new Vertex[]{
114 | vertexBottom2, vertexBottom1, vertexTop4, vertexTop3
115 | },
116 | this.textureOffsetX + depth,
117 | this.textureOffsetY + depth,
118 | this.textureOffsetX + depth + width,
119 | this.textureOffsetY + depth + height
120 | );
121 |
122 | this.polygons[5] = new Polygon(
123 | new Vertex[]{
124 | vertexBottom3, vertexBottom4, vertexTop1, vertexTop2
125 | },
126 | this.textureOffsetX + depth + width + depth,
127 | this.textureOffsetY + depth,
128 | this.textureOffsetX + depth + width + depth + width,
129 | this.textureOffsetY + depth + height
130 | );
131 |
132 | return this;
133 | }
134 |
135 | /**
136 | * Set the absolute position of the cube
137 | *
138 | * @param x Absolute x position of cube
139 | * @param y Absolute y position of cube
140 | * @param z Absolute z position of cube
141 | */
142 | public void setPosition(float x, float y, float z) {
143 | this.x = x;
144 | this.y = y;
145 | this.z = z;
146 | }
147 |
148 | /**
149 | * Render the cube
150 | */
151 | public void render() {
152 | glPushMatrix();
153 |
154 | // Position of the cube
155 | glTranslatef(this.x, this.y, this.z);
156 |
157 | // Rotation of the cube
158 | glRotated(Math.toDegrees(this.zRotation), 0.0F, 0.0F, 1.0F);
159 | glRotated(Math.toDegrees(this.yRotation), 0.0F, 1.0F, 0.0F);
160 | glRotated(Math.toDegrees(this.xRotation), 1.0F, 0.0F, 0.0F);
161 |
162 | // Start rendering
163 | glBegin(GL_QUADS);
164 |
165 | // Render polygons
166 | for (Polygon polygon : this.polygons) {
167 | polygon.render();
168 | }
169 |
170 | // Stop rendering
171 | glEnd();
172 |
173 | glPopMatrix();
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/src/main/java/com/mojang/minecraft/phys/AABB.java:
--------------------------------------------------------------------------------
1 | package com.mojang.minecraft.phys;
2 |
3 | public class AABB {
4 |
5 | private final double epsilon = 0.0F;
6 |
7 | public double minX;
8 | public double minY;
9 | public double minZ;
10 | public double maxX;
11 | public double maxY;
12 | public double maxZ;
13 |
14 | /**
15 | * Bounding box
16 | *
17 | * @param minX Minimum x side
18 | * @param minY Minimum y side
19 | * @param minZ Minimum z side
20 | * @param maxX Maximum x side
21 | * @param maxY Maximum y side
22 | * @param maxZ Maximum z side
23 | */
24 | public AABB(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
25 | this.minX = minX;
26 | this.minY = minY;
27 | this.minZ = minZ;
28 | this.maxX = maxX;
29 | this.maxY = maxY;
30 | this.maxZ = maxZ;
31 | }
32 |
33 | /**
34 | * Copy the current bounding box object
35 | *
36 | * @return Clone of the bounding box
37 | */
38 | public AABB clone() {
39 | return new AABB(this.minX, this.minY, this.minZ, this.maxX, this.maxY, this.maxZ);
40 | }
41 |
42 | /**
43 | * Expand the bounding box. Positive and negative numbers controls which side of the box should grow.
44 | *
45 | * @param x Amount to expand the minX or maxX
46 | * @param y Amount to expand the minY or maxY
47 | * @param z Amount to expand the minZ or maxZ
48 | * @return The expanded bounding box
49 | */
50 | public AABB expand(double x, double y, double z) {
51 | double minX = this.minX;
52 | double minY = this.minY;
53 | double minZ = this.minZ;
54 | double maxX = this.maxX;
55 | double maxY = this.maxY;
56 | double maxZ = this.maxZ;
57 |
58 | // Handle expanding of min/max x
59 | if (x < 0.0F) {
60 | minX += x;
61 | } else {
62 | maxX += x;
63 | }
64 |
65 | // Handle expanding of min/max y
66 | if (y < 0.0F) {
67 | minY += y;
68 | } else {
69 | maxY += y;
70 | }
71 |
72 | // Handle expanding of min/max z
73 | if (z < 0.0F) {
74 | minZ += z;
75 | } else {
76 | maxZ += z;
77 | }
78 |
79 | // Create new bounding box
80 | return new AABB(minX, minY, minZ, maxX, maxY, maxZ);
81 | }
82 |
83 | /**
84 | * Expand the bounding box on both sides.
85 | * The center is always fixed when using grow.
86 | *
87 | * @param x
88 | * @param y
89 | * @param z
90 | * @return
91 | */
92 | public AABB grow(double x, double y, double z) {
93 | return new AABB(this.minX - x, this.minY - y,
94 | this.minZ - z, this.maxX + x,
95 | this.maxY + y, this.maxZ + z);
96 | }
97 |
98 | /**
99 | * Check for collision on the X axis
100 | *
101 | * @param otherBoundingBox The other bounding box that is colliding with the this one.
102 | * @param x Position on the X axis that is colliding
103 | * @return Returns the corrected x position that collided.
104 | */
105 | public double clipXCollide(AABB otherBoundingBox, double x) {
106 | // Check if the boxes are colliding on the Y axis
107 | if (otherBoundingBox.maxY <= this.minY || otherBoundingBox.minY >= this.maxY) {
108 | return x;
109 | }
110 |
111 | // Check if the boxes are colliding on the Z axis
112 | if (otherBoundingBox.maxZ <= this.minZ || otherBoundingBox.minZ >= this.maxZ) {
113 | return x;
114 | }
115 |
116 | // Check for collision if the X axis of the current box is bigger
117 | if (x > 0.0F && otherBoundingBox.maxX <= this.minX) {
118 | double max = this.minX - otherBoundingBox.maxX - this.epsilon;
119 | if (max < x) {
120 | x = max;
121 | }
122 | }
123 |
124 | // Check for collision if the X axis of the current box is smaller
125 | if (x < 0.0F && otherBoundingBox.minX >= this.maxX) {
126 | double max = this.maxX - otherBoundingBox.minX + this.epsilon;
127 | if (max > x) {
128 | x = max;
129 | }
130 | }
131 |
132 | return x;
133 | }
134 |
135 | /**
136 | * Check for collision on the Y axis
137 | *
138 | * @param otherBoundingBox The other bounding box that is colliding with the this one.
139 | * @param y Position on the X axis that is colliding
140 | * @return Returns the corrected x position that collided.
141 | */
142 | public double clipYCollide(AABB otherBoundingBox, double y) {
143 | // Check if the boxes are colliding on the X axis
144 | if (otherBoundingBox.maxX <= this.minX || otherBoundingBox.minX >= this.maxX) {
145 | return y;
146 | }
147 |
148 | // Check if the boxes are colliding on the Z axis
149 | if (otherBoundingBox.maxZ <= this.minZ || otherBoundingBox.minZ >= this.maxZ) {
150 | return y;
151 | }
152 |
153 | // Check for collision if the Y axis of the current box is bigger
154 | if (y > 0.0F && otherBoundingBox.maxY <= this.minY) {
155 | double max = this.minY - otherBoundingBox.maxY - this.epsilon;
156 | if (max < y) {
157 | y = max;
158 | }
159 | }
160 |
161 | // Check for collision if the Y axis of the current box is bigger
162 | if (y < 0.0F && otherBoundingBox.minY >= this.maxY) {
163 | double max = this.maxY - otherBoundingBox.minY + this.epsilon;
164 | if (max > y) {
165 | y = max;
166 | }
167 | }
168 |
169 | return y;
170 | }
171 |
172 | /**
173 | * Check for collision on the Y axis
174 | *
175 | * @param otherBoundingBox The other bounding box that is colliding with the this one.
176 | * @param z Position on the X axis that is colliding
177 | * @return Returns the corrected x position that collided.
178 | */
179 | public double clipZCollide(AABB otherBoundingBox, double z) {
180 | // Check if the boxes are colliding on the X axis
181 | if (otherBoundingBox.maxX <= this.minX || otherBoundingBox.minX >= this.maxX) {
182 | return z;
183 | }
184 |
185 | // Check if the boxes are colliding on the Y axis
186 | if (otherBoundingBox.maxY <= this.minY || otherBoundingBox.minY >= this.maxY) {
187 | return z;
188 | }
189 |
190 | // Check for collision if the Z axis of the current box is bigger
191 | if (z > 0.0F && otherBoundingBox.maxZ <= this.minZ) {
192 | double max = this.minZ - otherBoundingBox.maxZ - this.epsilon;
193 | if (max < z) {
194 | z = max;
195 | }
196 | }
197 |
198 | // Check for collision if the Z axis of the current box is bigger
199 | if (z < 0.0F && otherBoundingBox.minZ >= this.maxZ) {
200 | double max = this.maxZ - otherBoundingBox.minZ + this.epsilon;
201 | if (max > z) {
202 | z = max;
203 | }
204 | }
205 |
206 | return z;
207 | }
208 |
209 | /**
210 | * Check if the two boxes are intersecting/overlapping
211 | *
212 | * @param otherBoundingBox The other bounding box that could intersect
213 | * @return The two boxes are overlapping
214 | */
215 | public boolean intersects(AABB otherBoundingBox) {
216 | // Check on X axis
217 | if (otherBoundingBox.maxX <= this.minX || otherBoundingBox.minX >= this.maxX) {
218 | return false;
219 | }
220 |
221 | // Check on Y axis
222 | if (otherBoundingBox.maxY <= this.minY || otherBoundingBox.minY >= this.maxY) {
223 | return false;
224 | }
225 |
226 | // Check on Z axis
227 | return (!(otherBoundingBox.maxZ <= this.minZ)) && (!(otherBoundingBox.minZ >= this.maxZ));
228 | }
229 |
230 | /**
231 | * Move the bounding box relative.
232 | *
233 | * @param x Relative offset x
234 | * @param y Relative offset y
235 | * @param z Relative offset z
236 | */
237 | public void move(double x, double y, double z) {
238 | this.minX += x;
239 | this.minY += y;
240 | this.minZ += z;
241 | this.maxX += x;
242 | this.maxY += y;
243 | this.maxZ += z;
244 | }
245 |
246 | /**
247 | * Create a new bounding box with the given offset
248 | *
249 | * @param x Relative offset x
250 | * @param y Relative offset x
251 | * @param z Relative offset x
252 | * @return New bounding box with the given offset relative to this bounding box
253 | */
254 | public AABB offset(double x, double y, double z) {
255 | return new AABB(this.minX + x, this.minY + y, this.minZ + z, this.maxX + x, this.maxY + y, this.maxZ + z);
256 | }
257 | }
258 |
--------------------------------------------------------------------------------
/src/main/java/com/mojang/minecraft/level/LevelRenderer.java:
--------------------------------------------------------------------------------
1 | package com.mojang.minecraft.level;
2 |
3 | import com.mojang.minecraft.HitResult;
4 | import com.mojang.minecraft.Player;
5 | import com.mojang.minecraft.Textures;
6 | import com.mojang.minecraft.level.tile.Tile;
7 | import com.mojang.minecraft.phys.AABB;
8 |
9 | import java.util.ArrayList;
10 | import java.util.List;
11 |
12 | import static org.lwjgl.opengl.GL11.*;
13 |
14 | public class LevelRenderer implements LevelListener {
15 |
16 | private static final int CHUNK_SIZE = 16;
17 |
18 | private final Tessellator tessellator;
19 | private final Level level;
20 | private final Chunk[] chunks;
21 |
22 | private final int chunkAmountX;
23 | private final int chunkAmountY;
24 | private final int chunkAmountZ;
25 |
26 | /**
27 | * Create renderer for level
28 | *
29 | * @param level The rendered level
30 | */
31 | public LevelRenderer(Level level) {
32 | level.addListener(this);
33 |
34 | this.tessellator = new Tessellator();
35 | this.level = level;
36 |
37 | // Calculate amount of chunks of level
38 | this.chunkAmountX = level.width / CHUNK_SIZE;
39 | this.chunkAmountY = level.depth / CHUNK_SIZE;
40 | this.chunkAmountZ = level.height / CHUNK_SIZE;
41 |
42 | // Create chunk array
43 | this.chunks = new Chunk[this.chunkAmountX * this.chunkAmountY * this.chunkAmountZ];
44 |
45 | // Fill level with chunks
46 | for (int x = 0; x < this.chunkAmountX; x++) {
47 | for (int y = 0; y < this.chunkAmountY; y++) {
48 | for (int z = 0; z < this.chunkAmountZ; z++) {
49 | // Calculate min bounds for chunk
50 | int minChunkX = x * CHUNK_SIZE;
51 | int minChunkY = y * CHUNK_SIZE;
52 | int minChunkZ = z * CHUNK_SIZE;
53 |
54 | // Calculate max bounds for chunk
55 | int maxChunkX = (x + 1) * CHUNK_SIZE;
56 | int maxChunkY = (y + 1) * CHUNK_SIZE;
57 | int maxChunkZ = (z + 1) * CHUNK_SIZE;
58 |
59 | // Check for chunk bounds out of level
60 | maxChunkX = Math.min(level.width, maxChunkX);
61 | maxChunkY = Math.min(level.depth, maxChunkY);
62 | maxChunkZ = Math.min(level.height, maxChunkZ);
63 |
64 | // Create chunk based on bounds
65 | Chunk chunk = new Chunk(level, minChunkX, minChunkY, minChunkZ, maxChunkX, maxChunkY, maxChunkZ);
66 | this.chunks[(x + y * this.chunkAmountX) * this.chunkAmountZ + z] = chunk;
67 | }
68 | }
69 | }
70 | }
71 |
72 | /**
73 | * Get all chunks with dirty flag
74 | *
75 | * @return List of dirty chunks
76 | */
77 | public List getAllDirtyChunks() {
78 | ArrayList dirty = new ArrayList<>();
79 | for (final Chunk chunk : this.chunks) {
80 | if (chunk.isDirty()) {
81 | dirty.add(chunk);
82 | }
83 | }
84 | return dirty;
85 | }
86 |
87 | /**
88 | * Render all chunks of the level
89 | *
90 | * @param layer The render layer
91 | */
92 | public void render(int layer) {
93 | glEnable(GL_TEXTURE_2D);
94 | glBindTexture(GL_TEXTURE_2D, Textures.loadTexture("/terrain.png", GL_NEAREST));
95 |
96 | // Get current camera frustum
97 | Frustum frustum = Frustum.getFrustum();
98 |
99 | // For all chunks
100 | for (Chunk chunk : this.chunks) {
101 |
102 | // Render if bounding box of chunk is in frustum
103 | if (frustum.isVisible(chunk.boundingBox)) {
104 |
105 | // Render chunk
106 | chunk.render(layer);
107 | }
108 | }
109 |
110 | glDisable(GL_TEXTURE_2D);
111 | }
112 |
113 | /**
114 | * Rebuild all dirty chunks in a sorted order
115 | *
116 | * @param player The player for the sort priority. Chunks closer to the player will get a higher priority.
117 | */
118 | public void updateDirtyChunks(Player player) {
119 | // Get all dirty chunks
120 | List dirty = getAllDirtyChunks();
121 | if (!dirty.isEmpty()) {
122 |
123 | // Sort the dirty chunk list
124 | dirty.sort(new DirtyChunkSorter(player, Frustum.getFrustum()));
125 |
126 | // Rebuild max 8 chunks per frame
127 | for (int i = 0; i < 8 && i < dirty.size(); i++) {
128 | dirty.get(i).rebuild();
129 | }
130 | }
131 | }
132 |
133 | /**
134 | * Mark all chunks inside of the given area as dirty.
135 | *
136 | * @param minX Minimum on X axis
137 | * @param minY Minimum on Y axis
138 | * @param minZ Minimum on Z axis
139 | * @param maxX Maximum on X axis
140 | * @param maxY Maximum on Y axis
141 | * @param maxZ Maximum on Z axis
142 | */
143 | public void setDirty(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) {
144 | // To chunk coordinates
145 | minX /= CHUNK_SIZE;
146 | minY /= CHUNK_SIZE;
147 | minZ /= CHUNK_SIZE;
148 | maxX /= CHUNK_SIZE;
149 | maxY /= CHUNK_SIZE;
150 | maxZ /= CHUNK_SIZE;
151 |
152 | // Minimum limit
153 | minX = Math.max(minX, 0);
154 | minY = Math.max(minY, 0);
155 | minZ = Math.max(minZ, 0);
156 |
157 | // Maximum limit
158 | maxX = Math.min(maxX, this.chunkAmountX - 1);
159 | maxY = Math.min(maxY, this.chunkAmountY - 1);
160 | maxZ = Math.min(maxZ, this.chunkAmountZ - 1);
161 |
162 | // Mark all chunks as dirty
163 | for (int x = minX; x <= maxX; x++) {
164 | for (int y = minY; y <= maxY; y++) {
165 | for (int z = minZ; z <= maxZ; z++) {
166 | // Get chunk at this position
167 | Chunk chunk = this.chunks[(x + y * this.chunkAmountX) * this.chunkAmountZ + z];
168 |
169 | // Set dirty
170 | chunk.setDirty();
171 | }
172 | }
173 | }
174 | }
175 |
176 | /**
177 | * Render pick selection face on tile
178 | *
179 | * @param player The player
180 | */
181 | public void pick(Player player, Frustum frustum) {
182 | float radius = 3.0F;
183 | AABB boundingBox = player.boundingBox.grow(radius, radius, radius);
184 |
185 | int minX = (int) boundingBox.minX;
186 | int maxX = (int) (boundingBox.maxX + 1.0F);
187 | int minY = (int) boundingBox.minY;
188 | int maxY = (int) (boundingBox.maxY + 1.0F);
189 | int minZ = (int) boundingBox.minZ;
190 | int maxZ = (int) (boundingBox.maxZ + 1.0F);
191 |
192 | glInitNames();
193 |
194 | // Define type
195 | glPushName(0); // Type 0
196 |
197 | // Define name value x
198 | glPushName(0);
199 |
200 | for (int x = minX; x < maxX; x++) {
201 | // Set last pushed value to x
202 | glLoadName(x);
203 |
204 | // Define name value y
205 | glPushName(0);
206 |
207 | for (int y = minY; y < maxY; y++) {
208 | // Set last pushed value to y
209 | glLoadName(y);
210 |
211 | // Define name value z
212 | glPushName(0);
213 |
214 | for (int z = minZ; z < maxZ; z++) {
215 | // Check for solid tile
216 | Tile tile = Tile.tiles[this.level.getTile(x, y, z)];
217 | if (tile != null && frustum.isVisible(tile.getTileAABB(x, y, z))) {
218 |
219 | // Set last pushed value to x
220 | glLoadName(z);
221 |
222 | // Define name value face id
223 | glPushName(0);
224 |
225 | // Render all faces
226 | for (int face = 0; face < 6; face++) {
227 |
228 | // Set last pushed value to face id
229 | glLoadName(face);
230 |
231 | // Render selection face
232 | this.tessellator.init();
233 | tile.renderFaceNoTexture(this.tessellator, x, y, z, face);
234 | this.tessellator.flush();
235 | }
236 |
237 | // Pop face id
238 | glPopName();
239 | }
240 | }
241 |
242 | // Pop z
243 | glPopName();
244 | }
245 |
246 | // Pop y
247 | glPopName();
248 | }
249 |
250 | // Pop x
251 | glPopName();
252 |
253 | // Pop type
254 | glPopName();
255 | }
256 |
257 | /**
258 | * Render hit face of the result
259 | *
260 | * @param hitResult The hit result to render
261 | */
262 | public void renderHit(HitResult hitResult) {
263 | // Setup blending and color
264 | glEnable(GL_BLEND);
265 | glBlendFunc(GL_SRC_ALPHA, GL_CURRENT_BIT);
266 | glColor4f(1.0F, 1.0F, 1.0F, ((float) Math.sin(System.currentTimeMillis() / 100.0D) * 0.2F + 0.4F) * 0.5F);
267 |
268 | // Render face
269 | this.tessellator.init();
270 | Tile.rock.renderFaceNoTexture(this.tessellator, hitResult.x, hitResult.y, hitResult.z, hitResult.face);
271 | this.tessellator.flush();
272 |
273 | // Disable blending
274 | glDisable(GL_BLEND);
275 | }
276 |
277 | @Override
278 | public void lightColumnChanged(int x, int z, int minY, int maxY) {
279 | setDirty(x - 1, minY - 1, z - 1, x + 1, maxY + 1, z + 1);
280 | }
281 |
282 | @Override
283 | public void tileChanged(int x, int y, int z) {
284 | setDirty(x - 1, y - 1, z - 1, x + 1, y + 1, z + 1);
285 | }
286 |
287 | @Override
288 | public void allChanged() {
289 | setDirty(0, 0, 0, this.level.width, this.level.depth, this.level.height);
290 | }
291 | }
292 |
--------------------------------------------------------------------------------
/src/main/java/com/mojang/minecraft/level/tile/Tile.java:
--------------------------------------------------------------------------------
1 | package com.mojang.minecraft.level.tile;
2 |
3 | import com.mojang.minecraft.level.Level;
4 | import com.mojang.minecraft.level.Tessellator;
5 | import com.mojang.minecraft.particle.Particle;
6 | import com.mojang.minecraft.particle.ParticleEngine;
7 | import com.mojang.minecraft.phys.AABB;
8 |
9 | import java.util.Random;
10 |
11 | public class Tile {
12 |
13 | // Tile array
14 | public static final Tile[] tiles = new Tile[256];
15 |
16 | public static Tile rock = new Tile(1, 1);
17 | public static Tile grass = new GrassTile(2);
18 | public static Tile dirt = new Tile(3, 2);
19 | public static Tile stoneBrick = new Tile(4, 16);
20 | public static Tile wood = new Tile(5, 4);
21 | public static Tile bush = new Bush(6);
22 |
23 | public final int id;
24 | protected int textureId;
25 |
26 | /**
27 | * Create tile using id
28 | *
29 | * @param id Id of the tile
30 | */
31 | public Tile(int id) {
32 | // Store tile in array
33 | Tile.tiles[id] = this;
34 | this.id = id;
35 | }
36 |
37 | /**
38 | * Create tile using id and texture slot id
39 | *
40 | * @param id Id of the tile
41 | * @param textureId Texture slot id
42 | */
43 | public Tile(int id, int textureId) {
44 | this(id);
45 |
46 | this.textureId = textureId;
47 | }
48 |
49 | /**
50 | * Render a tile at the given position
51 | *
52 | * @param tessellator Tessellator for rendering
53 | * @param level Level to check for surrounding tiles
54 | * @param layer The layer which decides if it's a shadow or not
55 | * @param x Tile position x
56 | * @param y Tile position y
57 | * @param z Tile position z
58 | */
59 | public void render(Tessellator tessellator, Level level, int layer, int x, int y, int z) {
60 | float shadeX = 0.6f;
61 | float shadeY = 1.0f;
62 | float shadeZ = 0.8f;
63 |
64 | // Render bottom face
65 | if (shouldRenderFace(level, x, y - 1, z, layer)) {
66 | tessellator.color(shadeY, shadeY, shadeY);
67 | renderFace(tessellator, x, y, z, 0);
68 | }
69 |
70 | // Render top face
71 | if (shouldRenderFace(level, x, y + 1, z, layer)) {
72 | tessellator.color(shadeY, shadeY, shadeY);
73 | renderFace(tessellator, x, y, z, 1);
74 | }
75 |
76 | // Render side faces Z
77 | if (shouldRenderFace(level, x, y, z - 1, layer)) {
78 | tessellator.color(shadeZ, shadeZ, shadeZ);
79 | renderFace(tessellator, x, y, z, 2);
80 | }
81 | if (shouldRenderFace(level, x, y, z + 1, layer)) {
82 | tessellator.color(shadeZ, shadeZ, shadeZ);
83 | renderFace(tessellator, x, y, z, 3);
84 | }
85 |
86 | // Render side faces X
87 | if (shouldRenderFace(level, x - 1, y, z, layer)) {
88 | tessellator.color(shadeX, shadeX, shadeX);
89 | renderFace(tessellator, x, y, z, 4);
90 | }
91 | if (shouldRenderFace(level, x + 1, y, z, layer)) {
92 | tessellator.color(shadeX, shadeX, shadeX);
93 | renderFace(tessellator, x, y, z, 5);
94 | }
95 | }
96 |
97 | private boolean shouldRenderFace(Level level, int x, int y, int z, int layer) {
98 | // Don't render face when both conditions are the same (isShadowLayer != isFullBrightness)
99 | return !level.isSolidTile(x, y, z) && (level.isLit(x, y, z) ^ layer == 1);
100 | }
101 |
102 | /**
103 | * Get the texture slot of the given face
104 | *
105 | * @param face Face id
106 | * @return The texture slot id
107 | */
108 | protected int getTexture(int face) {
109 | return this.textureId;
110 | }
111 |
112 | /**
113 | * Render the single face of a tile
114 | *
115 | * @param tessellator Tessellator for rendering
116 | * @param x Tile position x
117 | * @param y Tile position y
118 | * @param z Tile position z
119 | * @param face Face id (0:Top, 1:Bottom, ...)
120 | */
121 | public void renderFace(Tessellator tessellator, int x, int y, int z, int face) {
122 | // Get texture slot id of this face
123 | int textureId = getTexture(face);
124 |
125 | // UV mapping points
126 | float minU = textureId % 16 / 16.0F;
127 | float maxU = minU + 16 / 256F;
128 | float minV = (float) (textureId / 16) / 16.0F;
129 | float maxV = minV + 16 / 256F;
130 |
131 | // Vertex points
132 | float minX = x + 0.0f;
133 | float maxX = x + 1.0f;
134 | float minY = y + 0.0f;
135 | float maxY = y + 1.0f;
136 | float minZ = z + 0.0f;
137 | float maxZ = z + 1.0f;
138 |
139 | // Render bottom face
140 | if (face == 0) {
141 | tessellator.vertexUV(minX, minY, maxZ, minU, maxV);
142 | tessellator.vertexUV(minX, minY, minZ, minU, minV);
143 | tessellator.vertexUV(maxX, minY, minZ, maxU, minV);
144 | tessellator.vertexUV(maxX, minY, maxZ, maxU, maxV);
145 | }
146 |
147 | // Render top face
148 | if (face == 1) {
149 | tessellator.vertexUV(maxX, maxY, maxZ, maxU, maxV);
150 | tessellator.vertexUV(maxX, maxY, minZ, maxU, minV);
151 | tessellator.vertexUV(minX, maxY, minZ, minU, minV);
152 | tessellator.vertexUV(minX, maxY, maxZ, minU, maxV);
153 | }
154 |
155 | // Render side faces Z
156 | if (face == 2) {
157 | tessellator.vertexUV(minX, maxY, minZ, maxU, minV);
158 | tessellator.vertexUV(maxX, maxY, minZ, minU, minV);
159 | tessellator.vertexUV(maxX, minY, minZ, minU, maxV);
160 | tessellator.vertexUV(minX, minY, minZ, maxU, maxV);
161 | }
162 | if (face == 3) {
163 | tessellator.vertexUV(minX, maxY, maxZ, minU, minV);
164 | tessellator.vertexUV(minX, minY, maxZ, minU, maxV);
165 | tessellator.vertexUV(maxX, minY, maxZ, maxU, maxV);
166 | tessellator.vertexUV(maxX, maxY, maxZ, maxU, minV);
167 | }
168 |
169 | // Render side faces X
170 | if (face == 4) {
171 | tessellator.vertexUV(minX, maxY, maxZ, maxU, minV);
172 | tessellator.vertexUV(minX, maxY, minZ, minU, minV);
173 | tessellator.vertexUV(minX, minY, minZ, minU, maxV);
174 | tessellator.vertexUV(minX, minY, maxZ, maxU, maxV);
175 | }
176 | if (face == 5) {
177 | tessellator.vertexUV(maxX, minY, maxZ, minU, maxV);
178 | tessellator.vertexUV(maxX, minY, minZ, maxU, maxV);
179 | tessellator.vertexUV(maxX, maxY, minZ, maxU, minV);
180 | tessellator.vertexUV(maxX, maxY, maxZ, minU, minV);
181 | }
182 | }
183 |
184 | /**
185 | * Render the single face of a tile without a texture
186 | *
187 | * @param tessellator Tessellator for rendering
188 | * @param x Tile position x
189 | * @param y Tile position y
190 | * @param z Tile position z
191 | * @param face Face id (0:Top, 1:Bottom, ...)
192 | */
193 | public void renderFaceNoTexture(Tessellator tessellator, int x, int y, int z, int face) {
194 | float minX = x + 0.0f;
195 | float maxX = x + 1.0f;
196 | float minY = y + 0.0f;
197 | float maxY = y + 1.0f;
198 | float minZ = z + 0.0f;
199 | float maxZ = z + 1.0f;
200 |
201 | // Render face
202 | if (face == 0) {
203 | tessellator.vertex(minX, minY, maxZ);
204 | tessellator.vertex(minX, minY, minZ);
205 | tessellator.vertex(maxX, minY, minZ);
206 | tessellator.vertex(maxX, minY, maxZ);
207 | }
208 | if (face == 1) {
209 | tessellator.vertex(maxX, maxY, maxZ);
210 | tessellator.vertex(maxX, maxY, minZ);
211 | tessellator.vertex(minX, maxY, minZ);
212 | tessellator.vertex(minX, maxY, maxZ);
213 | }
214 | if (face == 2) {
215 | tessellator.vertex(minX, maxY, minZ);
216 | tessellator.vertex(maxX, maxY, minZ);
217 | tessellator.vertex(maxX, minY, minZ);
218 | tessellator.vertex(minX, minY, minZ);
219 | }
220 | if (face == 3) {
221 | tessellator.vertex(minX, maxY, maxZ);
222 | tessellator.vertex(minX, minY, maxZ);
223 | tessellator.vertex(maxX, minY, maxZ);
224 | tessellator.vertex(maxX, maxY, maxZ);
225 | }
226 | if (face == 4) {
227 | tessellator.vertex(minX, maxY, maxZ);
228 | tessellator.vertex(minX, maxY, minZ);
229 | tessellator.vertex(minX, minY, minZ);
230 | tessellator.vertex(minX, minY, maxZ);
231 | }
232 | if (face == 5) {
233 | tessellator.vertex(maxX, minY, maxZ);
234 | tessellator.vertex(maxX, minY, minZ);
235 | tessellator.vertex(maxX, maxY, minZ);
236 | tessellator.vertex(maxX, maxY, maxZ);
237 | }
238 | }
239 |
240 | /**
241 | * Ticked randomly
242 | *
243 | * @param level Level instance
244 | * @param x Position x of the tile
245 | * @param y Position y of the tile
246 | * @param z Position z of the tile
247 | * @param random Random instance
248 | */
249 | public void onTick(Level level, int x, int y, int z, Random random) {
250 | // No implementation
251 | }
252 |
253 | /**
254 | * Called when a tile gets destroyed by the player
255 | *
256 | * @param level The current level
257 | * @param x Tile x location
258 | * @param y Tile y location
259 | * @param z Tile z location
260 | * @param particleEngine ParticleEngine to create the particles
261 | */
262 | public void onDestroy(Level level, int x, int y, int z, ParticleEngine particleEngine) {
263 | int spread = 4;
264 |
265 | // Spread particles in a cube
266 | for (int offsetX = 0; offsetX < spread; offsetX++) {
267 | for (int offsetY = 0; offsetY < spread; offsetY++) {
268 | for (int offsetZ = 0; offsetZ < spread; offsetZ++) {
269 |
270 | float targetX = x + (offsetX + 0.5F) / spread;
271 | float targetY = y + (offsetY + 0.5F) / spread;
272 | float targetZ = z + (offsetZ + 0.5F) / spread;
273 |
274 | float motionX = targetX - x - 0.5F;
275 | float motionY = targetY - y - 0.5F;
276 | float motionZ = targetZ - z - 0.5F;
277 |
278 | // Add particle to the engine
279 | Particle particle = new Particle(level, targetX, targetY, targetZ, motionX, motionY, motionZ, this.textureId);
280 | particleEngine.add(particle);
281 | }
282 | }
283 | }
284 | }
285 |
286 | /**
287 | * Get bounding box of the entire cube
288 | *
289 | * @param x Tile position x
290 | * @param y Tile position y
291 | * @param z Tile position z
292 | * @return Bounding box of the entire cube
293 | */
294 | public AABB getAABB(int x, int y, int z) {
295 | return new AABB(x, y, z, x + 1, y + 1, z + 1);
296 | }
297 |
298 | /**
299 | * Get bounding box of the actual model
300 | *
301 | * @param x Tile position x
302 | * @param y Tile position y
303 | * @param z Tile position z
304 | * @return Actual bounding box of the tile
305 | */
306 | public AABB getTileAABB(int x, int y, int z) {
307 | return new AABB(x, y, z, x + 1, y + 1, z + 1);
308 | }
309 |
310 | /**
311 | * Tile is blocking light of the sun
312 | *
313 | * @return Returns true if the tile can block the light of the sun
314 | */
315 | public boolean blocksLight() {
316 | return true;
317 | }
318 |
319 | /**
320 | * Tile is solid
321 | *
322 | * @return Returns true if the tile is a solid type
323 | */
324 | public boolean isSolid() {
325 | return true;
326 | }
327 | }
328 |
--------------------------------------------------------------------------------
/src/main/java/com/mojang/minecraft/level/Level.java:
--------------------------------------------------------------------------------
1 | package com.mojang.minecraft.level;
2 |
3 | import com.mojang.minecraft.level.tile.Tile;
4 | import com.mojang.minecraft.phys.AABB;
5 |
6 | import java.io.DataInputStream;
7 | import java.io.DataOutputStream;
8 | import java.io.FileInputStream;
9 | import java.io.FileOutputStream;
10 | import java.util.ArrayList;
11 | import java.util.Random;
12 | import java.util.zip.GZIPInputStream;
13 | import java.util.zip.GZIPOutputStream;
14 |
15 | public class Level {
16 |
17 | public final int width;
18 | public final int height;
19 | public final int depth;
20 |
21 | private final byte[] blocks;
22 | private final int[] lightDepths;
23 |
24 | private final ArrayList levelListeners = new ArrayList<>();
25 | private final Random random = new Random();
26 |
27 | /**
28 | * Three dimensional level containing all tiles
29 | *
30 | * @param width Level width
31 | * @param height Level height
32 | * @param depth Level depth
33 | */
34 | public Level(int width, int height, int depth) {
35 | this.width = width;
36 | this.height = height;
37 | this.depth = depth;
38 |
39 | this.blocks = new byte[width * height * depth];
40 | this.lightDepths = new int[width * height];
41 |
42 | // Load level if it exists
43 | boolean mapLoaded = load();
44 |
45 | // Generate a new level if file doesn't exists
46 | if (!mapLoaded) {
47 | generateMap();
48 | }
49 |
50 | // Calculate light depth of the entire level
51 | calcLightDepths(0, 0, width, height);
52 | }
53 |
54 | /**
55 | * Generate a new level
56 | */
57 | private void generateMap() {
58 | int[] firstHeightMap = new PerlinNoiseFilter(0).read(this.width, this.height);
59 | int[] secondHeightMap = new PerlinNoiseFilter(0).read(this.width, this.height);
60 | int[] cliffMap = new PerlinNoiseFilter(1).read(this.width, this.height);
61 | int[] rockMap = new PerlinNoiseFilter(1).read(this.width, this.height);
62 |
63 | // Generate tiles
64 | for (int x = 0; x < this.width; ++x) {
65 | for (int y = 0; y < this.depth; ++y) {
66 | for (int z = 0; z < this.height; ++z) {
67 | // Extract values from height map
68 | int firstHeightValue = firstHeightMap[x + z * this.width];
69 | int secondHeightValue = secondHeightMap[x + z * this.width];
70 |
71 | // Change the height map
72 | if (cliffMap[x + z * this.width] < 128) {
73 | secondHeightValue = firstHeightValue;
74 | }
75 |
76 | // Get max level height at this position
77 | int maxLevelHeight = Math.max(secondHeightValue, firstHeightValue) / 8 + this.depth / 3;
78 |
79 | // Get end of rock layer
80 | int maxRockHeight = rockMap[x + z * this.width] / 8 + this.depth / 3;
81 |
82 | // Keep it below the max height of the level
83 | if (maxRockHeight > maxLevelHeight - 2) {
84 | maxRockHeight = maxLevelHeight - 2;
85 | }
86 |
87 | // Get block array index
88 | int index = (y * this.height + z) * this.width + x;
89 |
90 | int id = 0;
91 |
92 | // Grass layer
93 | if (y == maxLevelHeight) {
94 | id = Tile.grass.id;
95 | }
96 |
97 | // Dirt layer
98 | if (y < maxLevelHeight) {
99 | id = Tile.dirt.id;
100 | }
101 |
102 | // Rock layer
103 | if (y <= maxRockHeight) {
104 | id = Tile.rock.id;
105 | }
106 |
107 | // Set the tile id
108 | this.blocks[index] = (byte) id;
109 | }
110 | }
111 | }
112 | }
113 |
114 | /**
115 | * Load blocks from level.dat
116 | *
117 | * @return successfully loaded
118 | */
119 | public boolean load() {
120 | try {
121 | DataInputStream dis = new DataInputStream(new GZIPInputStream(new FileInputStream("level.dat")));
122 | dis.readFully(this.blocks);
123 | calcLightDepths(0, 0, this.width, this.height);
124 | dis.close();
125 |
126 | // Notify all tiles changed
127 | for (LevelListener levelListener : this.levelListeners) {
128 | levelListener.allChanged();
129 | }
130 |
131 | return true;
132 | } catch (Exception e) {
133 | e.printStackTrace();
134 |
135 | return false;
136 | }
137 | }
138 |
139 | /**
140 | * Store blocks in level.dat
141 | */
142 | public void save() {
143 | try {
144 | DataOutputStream dos = new DataOutputStream(new GZIPOutputStream(new FileOutputStream("level.dat")));
145 | dos.write(this.blocks);
146 | dos.close();
147 | } catch (Exception e) {
148 | e.printStackTrace();
149 | }
150 | }
151 |
152 | /**
153 | * Calculate light depth of given area
154 | *
155 | * @param minX Minimum on X axis
156 | * @param minZ Minimum on Z axis
157 | * @param maxX Maximum on X axis
158 | * @param maxZ Maximum on Z axis
159 | */
160 | private void calcLightDepths(int minX, int minZ, int maxX, int maxZ) {
161 | // For each x/z position in level
162 | for (int x = minX; x < minX + maxX; x++) {
163 | for (int z = minZ; z < minZ + maxZ; z++) {
164 |
165 | // Get previous light depth value
166 | int prevDepth = this.lightDepths[x + z * this.width];
167 |
168 | // Calculate new light depth
169 | int depth = this.depth - 1;
170 | while (depth > 0 && !isLightBlocker(x, depth, z)) {
171 | depth--;
172 | }
173 |
174 | // Set new light depth
175 | this.lightDepths[x + z * this.width] = depth;
176 |
177 | // On light depth change
178 | if (prevDepth != depth) {
179 | // Get changed range
180 | int minTileChangeY = Math.min(prevDepth, depth);
181 | int maxTileChangeY = Math.max(prevDepth, depth);
182 |
183 | // Notify tile column changed
184 | for (LevelListener levelListener : this.levelListeners) {
185 | levelListener.lightColumnChanged(x, z, minTileChangeY, maxTileChangeY);
186 | }
187 | }
188 | }
189 | }
190 | }
191 |
192 | /**
193 | * Return true if a tile is available at the given location
194 | *
195 | * @param x Level position x
196 | * @param y Level position y
197 | * @param z Level position z
198 | * @return Tile available
199 | */
200 | public boolean isTile(int x, int y, int z) {
201 | // Is location out of the level?
202 | if (x < 0 || y < 0 || z < 0 || x >= this.width || y >= this.depth || z >= this.height) {
203 | return false;
204 | }
205 |
206 | // Calculate index from x, y and z
207 | int index = (y * this.height + z) * this.width + x;
208 |
209 | // Return true if there is a tile at this location
210 | return this.blocks[index] != 0;
211 | }
212 |
213 | /**
214 | * Return the id of the tile at the given location
215 | *
216 | * @param x Level position x
217 | * @param y Level position y
218 | * @param z Level position z
219 | * @return Tile id at this location
220 | */
221 | public int getTile(int x, int y, int z) {
222 | // Is location out of the level?
223 | if (x < 0 || y < 0 || z < 0 || x >= this.width || y >= this.depth || z >= this.height) {
224 | return 0;
225 | }
226 |
227 | // Calculate index from x, y and z
228 | int index = (y * this.height + z) * this.width + x;
229 |
230 | // Return tile id
231 | return this.blocks[index];
232 | }
233 |
234 | /**
235 | * Returns true if tile is solid and not transparent
236 | *
237 | * @param x Tile position x
238 | * @param y Tile position y
239 | * @param z Tile position z
240 | * @return Tile is solid
241 | */
242 | public boolean isSolidTile(int x, int y, int z) {
243 | Tile tile = Tile.tiles[this.getTile(x, y, z)];
244 | return tile != null && tile.isSolid();
245 | }
246 |
247 | /**
248 | * Returns true if the tile is blocking the light
249 | *
250 | * @param x Tile position x
251 | * @param y Tile position y
252 | * @param z Tile position z
253 | * @return Tile blocks the light
254 | */
255 | public boolean isLightBlocker(final int x, final int y, final int z) {
256 | Tile tile = Tile.tiles[this.getTile(x, y, z)];
257 | return tile != null && tile.blocksLight();
258 | }
259 |
260 | /**
261 | * Get bounding box of all tiles surrounded by the given bounding box
262 | *
263 | * @param boundingBox Target bounding box located in the level
264 | * @return List of bounding boxes representing the tiles around the given bounding box
265 | */
266 | public ArrayList getCubes(AABB boundingBox) {
267 | ArrayList boundingBoxList = new ArrayList<>();
268 |
269 | int minX = (int) (Math.floor(boundingBox.minX) - 1);
270 | int maxX = (int) (Math.ceil(boundingBox.maxX) + 1);
271 | int minY = (int) (Math.floor(boundingBox.minY) - 1);
272 | int maxY = (int) (Math.ceil(boundingBox.maxY) + 1);
273 | int minZ = (int) (Math.floor(boundingBox.minZ) - 1);
274 | int maxZ = (int) (Math.ceil(boundingBox.maxZ) + 1);
275 |
276 | // Minimum level position
277 | minX = Math.max(0, minX);
278 | minY = Math.max(0, minY);
279 | minZ = Math.max(0, minZ);
280 |
281 | // Maximum level position
282 | maxX = Math.min(this.width, maxX);
283 | maxY = Math.min(this.depth, maxY);
284 | maxZ = Math.min(this.height, maxZ);
285 |
286 | // Include all surrounding tiles
287 | for (int x = minX; x < maxX; x++) {
288 | for (int y = minY; y < maxY; y++) {
289 | for (int z = minZ; z < maxZ; z++) {
290 |
291 | // Get tile this location
292 | Tile tile = Tile.tiles[this.getTile(x, y, z)];
293 | if (tile != null) {
294 |
295 | // Get bounding box of the the tile
296 | AABB aabb = tile.getAABB(x, y, z);
297 | if (aabb != null) {
298 | boundingBoxList.add(aabb);
299 | }
300 | }
301 | }
302 | }
303 | }
304 | return boundingBoxList;
305 | }
306 |
307 |
308 | /**
309 | * Set tile at position
310 | *
311 | * @param x Tile position x
312 | * @param y Tile position y
313 | * @param z Tile position z
314 | * @param id Type of tile
315 | * @return Tile changed
316 | */
317 | public boolean setTile(int x, int y, int z, int id) {
318 | // Check if position is out of level
319 | if (x < 0 || y < 0 || z < 0 || x >= this.width || y >= this.depth || z >= this.height) {
320 | return false;
321 | }
322 |
323 | // Get index of this position
324 | int index = (y * this.height + z) * this.width + x;
325 |
326 | // Check if type changed
327 | if (id == this.blocks[index])
328 | return false;
329 |
330 | // Set tile
331 | this.blocks[index] = (byte) id;
332 |
333 | // Update lightning
334 | this.calcLightDepths(x, z, 1, 1);
335 |
336 | // Notify tile changed
337 | for (LevelListener levelListener : this.levelListeners) {
338 | levelListener.tileChanged(x, y, z);
339 | }
340 |
341 | return true;
342 | }
343 |
344 | /**
345 | * Register a level listener
346 | *
347 | * @param levelListener Listener interface
348 | */
349 | public void addListener(LevelListener levelListener) {
350 | this.levelListeners.add(levelListener);
351 | }
352 |
353 | /**
354 | * Check if the given tile position is in the sun
355 | *
356 | * @param x Tile position x
357 | * @param y Tile position y
358 | * @param z Tile position z
359 | * @return Tile is in the sun
360 | */
361 | public boolean isLit(int x, int y, int z) {
362 | return x < 0 || y < 0 || z < 0 || x >= this.width || y >= this.depth || z >= this.height || y >= this.lightDepths[x + z * this.width];
363 | }
364 |
365 | /**
366 | * Tick a random tile in the level
367 | */
368 | public void onTick() {
369 | // Amount of tiles in this level
370 | int totalTiles = this.width * this.height * this.depth;
371 |
372 | // Amount of tiles to process for this tick
373 | int ticks = totalTiles / 400;
374 |
375 | // Tick multiple tiles in one game tick
376 | for (int i = 0; i < ticks; ++i) {
377 | // Get random position of the tile
378 | int x = this.random.nextInt(this.width);
379 | int y = this.random.nextInt(this.depth);
380 | int z = this.random.nextInt(this.height);
381 |
382 | // Get tile type
383 | Tile tile = Tile.tiles[this.getTile(x, y, z)];
384 | if (tile != null) {
385 | // Tick tile
386 | tile.onTick(this, x, y, z, this.random);
387 | }
388 | }
389 | }
390 | }
391 |
--------------------------------------------------------------------------------
/src/main/java/com/mojang/minecraft/level/Frustum.java:
--------------------------------------------------------------------------------
1 | package com.mojang.minecraft.level;
2 |
3 | import com.mojang.minecraft.phys.AABB;
4 | import org.lwjgl.BufferUtils;
5 |
6 | import java.nio.FloatBuffer;
7 |
8 | import static org.lwjgl.opengl.GL11.*;
9 |
10 | /*
11 | * Author: Ben Humphrey (DigiBen)
12 | * E-mail: digiben@gametutorials.com
13 | */
14 | public class Frustum {
15 |
16 | // We create an enum of the sides so we don't have to call each side 0 or 1.
17 | // This way it makes it more understandable and readable when dealing with frustum sides.
18 | public static final int RIGHT = 0; // The RIGHT side of the frustum
19 | public static final int LEFT = 1; // The LEFT side of the frustum
20 | public static final int BOTTOM = 2; // The BOTTOM side of the frustum
21 | public static final int TOP = 3; // The TOP side of the frustum
22 | public static final int BACK = 4; // The BACK side of the frustum
23 | public static final int FRONT = 5; // The FRONT side of the frustum
24 |
25 | // Like above, instead of saying a number for the ABC and D of the plane, we
26 | // want to be more descriptive.
27 | public static final int A = 0; // The X value of the plane's normal
28 | public static final int B = 1; // The Y value of the plane's normal
29 | public static final int C = 2; // The Z value of the plane's normal
30 | public static final int D = 3; // The distance the plane is from the origin
31 |
32 | private static Frustum frustum = new Frustum();
33 |
34 | // This holds the A B C and D values for each side of our frustum.
35 | float[][] m_Frustum = new float[6][4];
36 |
37 | /**
38 | * FloatBuffer to get ModelView matrix.
39 | **/
40 | FloatBuffer modl_b;
41 |
42 | /**
43 | * FloatBuffer to get Projection matrix.
44 | **/
45 | FloatBuffer proj_b;
46 |
47 |
48 | /**
49 | * Frustum constructor.
50 | */
51 | public Frustum() {
52 | modl_b = BufferUtils.createFloatBuffer(16);
53 | proj_b = BufferUtils.createFloatBuffer(16);
54 | }
55 |
56 | /**
57 | * Calculate the frustum and return it
58 | *
59 | * @return Current frustum
60 | */
61 | public static Frustum getFrustum() {
62 | Frustum.frustum.calculateFrustum();
63 | return Frustum.frustum;
64 | }
65 |
66 | ///////////////////////////////// NORMALIZE PLANE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
67 | /////
68 | ///// This normalizes a plane (A side) from a given frustum.
69 | /////
70 | ///////////////////////////////// NORMALIZE PLANE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
71 | public void normalizePlane(float[][] frustum, int side) {
72 | // Here we calculate the magnitude of the normal to the plane (point A B C)
73 | // Remember that (A, B, C) is that same thing as the normal's (X, Y, Z).
74 | // To calculate magnitude you use the equation: magnitude = sqrt( x^2 + y^2 + z^2)
75 | float magnitude = (float) Math.sqrt(frustum[side][A] * frustum[side][A] +
76 | frustum[side][B] * frustum[side][B] + frustum[side][C] * frustum[side][C]);
77 |
78 | // Then we divide the plane's values by it's magnitude.
79 | // This makes it easier to work with.
80 | frustum[side][A] /= magnitude;
81 | frustum[side][B] /= magnitude;
82 | frustum[side][C] /= magnitude;
83 | frustum[side][D] /= magnitude;
84 | }
85 |
86 |
87 | ///////////////////////////////// CALCULATE FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
88 | /////
89 | ///// This extracts our frustum from the projection and modelview matrix.
90 | /////
91 | ///////////////////////////////// CALCULATE FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
92 | public void calculateFrustum() {
93 | float[] proj = new float[16]; // This will hold our projection matrix
94 | float[] modl = new float[16]; // This will hold our modelview matrix
95 | float[] clip = new float[16]; // This will hold the clipping planes
96 |
97 |
98 | // glGetFloat() is used to extract information about our OpenGL world.
99 | // Below, we pass in GL_PROJECTION_MATRIX to abstract our projection matrix.
100 | // It then stores the matrix into an array of [16].
101 | proj_b.rewind();
102 | glGetFloat(GL_PROJECTION_MATRIX, proj_b);
103 | proj_b.rewind();
104 | proj_b.get(proj);
105 |
106 | // By passing in GL_MODELVIEW_MATRIX, we can abstract our model view matrix.
107 | // This also stores it in an array of [16].
108 | modl_b.rewind();
109 | glGetFloat(GL_MODELVIEW_MATRIX, modl_b);
110 | modl_b.rewind();
111 | modl_b.get(modl);
112 |
113 | // Now that we have our modelview and projection matrix, if we combine these 2 matrices,
114 | // it will give us our clipping planes. To combine 2 matrices, we multiply them.
115 |
116 | clip[0] = modl[0] * proj[0] + modl[1] * proj[4] + modl[2] * proj[8] + modl[3] * proj[12];
117 | clip[1] = modl[0] * proj[1] + modl[1] * proj[5] + modl[2] * proj[9] + modl[3] * proj[13];
118 | clip[2] = modl[0] * proj[2] + modl[1] * proj[6] + modl[2] * proj[10] + modl[3] * proj[14];
119 | clip[3] = modl[0] * proj[3] + modl[1] * proj[7] + modl[2] * proj[11] + modl[3] * proj[15];
120 |
121 | clip[4] = modl[4] * proj[0] + modl[5] * proj[4] + modl[6] * proj[8] + modl[7] * proj[12];
122 | clip[5] = modl[4] * proj[1] + modl[5] * proj[5] + modl[6] * proj[9] + modl[7] * proj[13];
123 | clip[6] = modl[4] * proj[2] + modl[5] * proj[6] + modl[6] * proj[10] + modl[7] * proj[14];
124 | clip[7] = modl[4] * proj[3] + modl[5] * proj[7] + modl[6] * proj[11] + modl[7] * proj[15];
125 |
126 | clip[8] = modl[8] * proj[0] + modl[9] * proj[4] + modl[10] * proj[8] + modl[11] * proj[12];
127 | clip[9] = modl[8] * proj[1] + modl[9] * proj[5] + modl[10] * proj[9] + modl[11] * proj[13];
128 | clip[10] = modl[8] * proj[2] + modl[9] * proj[6] + modl[10] * proj[10] + modl[11] * proj[14];
129 | clip[11] = modl[8] * proj[3] + modl[9] * proj[7] + modl[10] * proj[11] + modl[11] * proj[15];
130 |
131 | clip[12] = modl[12] * proj[0] + modl[13] * proj[4] + modl[14] * proj[8] + modl[15] * proj[12];
132 | clip[13] = modl[12] * proj[1] + modl[13] * proj[5] + modl[14] * proj[9] + modl[15] * proj[13];
133 | clip[14] = modl[12] * proj[2] + modl[13] * proj[6] + modl[14] * proj[10] + modl[15] * proj[14];
134 | clip[15] = modl[12] * proj[3] + modl[13] * proj[7] + modl[14] * proj[11] + modl[15] * proj[15];
135 |
136 | // Now we actually want to get the sides of the frustum. To do this we take
137 | // the clipping planes we received above and extract the sides from them.
138 |
139 | // This will extract the RIGHT side of the frustum
140 | m_Frustum[RIGHT][A] = clip[3] - clip[0];
141 | m_Frustum[RIGHT][B] = clip[7] - clip[4];
142 | m_Frustum[RIGHT][C] = clip[11] - clip[8];
143 | m_Frustum[RIGHT][D] = clip[15] - clip[12];
144 |
145 | // Now that we have a normal (A,B,C) and a distance (D) to the plane,
146 | // we want to normalize that normal and distance.
147 |
148 | // Normalize the RIGHT side
149 | normalizePlane(m_Frustum, RIGHT);
150 |
151 | // This will extract the LEFT side of the frustum
152 | m_Frustum[LEFT][A] = clip[3] + clip[0];
153 | m_Frustum[LEFT][B] = clip[7] + clip[4];
154 | m_Frustum[LEFT][C] = clip[11] + clip[8];
155 | m_Frustum[LEFT][D] = clip[15] + clip[12];
156 |
157 | // Normalize the LEFT side
158 | normalizePlane(m_Frustum, LEFT);
159 |
160 | // This will extract the BOTTOM side of the frustum
161 | m_Frustum[BOTTOM][A] = clip[3] + clip[1];
162 | m_Frustum[BOTTOM][B] = clip[7] + clip[5];
163 | m_Frustum[BOTTOM][C] = clip[11] + clip[9];
164 | m_Frustum[BOTTOM][D] = clip[15] + clip[13];
165 |
166 | // Normalize the BOTTOM side
167 | normalizePlane(m_Frustum, BOTTOM);
168 |
169 | // This will extract the TOP side of the frustum
170 | m_Frustum[TOP][A] = clip[3] - clip[1];
171 | m_Frustum[TOP][B] = clip[7] - clip[5];
172 | m_Frustum[TOP][C] = clip[11] - clip[9];
173 | m_Frustum[TOP][D] = clip[15] - clip[13];
174 |
175 | // Normalize the TOP side
176 | normalizePlane(m_Frustum, TOP);
177 |
178 | // This will extract the BACK side of the frustum
179 | m_Frustum[BACK][A] = clip[3] - clip[2];
180 | m_Frustum[BACK][B] = clip[7] - clip[6];
181 | m_Frustum[BACK][C] = clip[11] - clip[10];
182 | m_Frustum[BACK][D] = clip[15] - clip[14];
183 |
184 | // Normalize the BACK side
185 | normalizePlane(m_Frustum, BACK);
186 |
187 | // This will extract the FRONT side of the frustum
188 | m_Frustum[FRONT][A] = clip[3] + clip[2];
189 | m_Frustum[FRONT][B] = clip[7] + clip[6];
190 | m_Frustum[FRONT][C] = clip[11] + clip[10];
191 | m_Frustum[FRONT][D] = clip[15] + clip[14];
192 |
193 | // Normalize the FRONT side
194 | normalizePlane(m_Frustum, FRONT);
195 | }
196 |
197 | // The code below will allow us to make checks within the frustum. For example,
198 | // if we want to see if a point, a sphere, or a cube lies inside of the frustum.
199 | // Because all of our planes point INWARDS (The normals are all pointing inside the frustum)
200 | // we then can assume that if a point is in FRONT of all of the planes, it's inside.
201 |
202 | ///////////////////////////////// POINT IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
203 | /////
204 | ///// This determines if a point is inside of the frustum
205 | /////
206 | ///////////////////////////////// POINT IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
207 | public boolean pointInFrustum(float x, float y, float z) {
208 | // Go through all the sides of the frustum
209 | for (int i = 0; i < 6; i++) {
210 | // Calculate the plane equation and check if the point is behind a side of the frustum
211 | if (m_Frustum[i][A] * x + m_Frustum[i][B] * y + m_Frustum[i][C] * z + m_Frustum[i][D] <= 0) {
212 | // The point was behind a side, so it ISN'T in the frustum
213 | return false;
214 | }
215 | }
216 |
217 | // The point was inside of the frustum (In front of ALL the sides of the frustum)
218 | return true;
219 | }
220 |
221 |
222 | ///////////////////////////////// SPHERE IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
223 | /////
224 | ///// This determines if a sphere is inside of our frustum by it's center and radius.
225 | /////
226 | ///////////////////////////////// SPHERE IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
227 | public boolean sphereInFrustum(float x, float y, float z, float radius) {
228 | // Go through all the sides of the frustum
229 | for (int i = 0; i < 6; i++) {
230 | // If the center of the sphere is farther away from the plane than the radius
231 | if (m_Frustum[i][A] * x + m_Frustum[i][B] * y + m_Frustum[i][C] * z + m_Frustum[i][D] <= -radius) {
232 | // The distance was greater than the radius so the sphere is outside of the frustum
233 | return false;
234 | }
235 | }
236 |
237 | // The sphere was inside of the frustum!
238 | return true;
239 | }
240 |
241 | ///////////////////////////////// CUBE IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
242 | /////
243 | ///// This determines if a cube is in or around our frustum by it's center and 1/2 it's length
244 | /////
245 | ///////////////////////////////// CUBE IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*
246 | public boolean cubeInFrustum(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) {
247 | // This test is a bit more work, but not too much more complicated.
248 | // Basically, what is going on is, that we are given the center of the cube,
249 | // and half the length. Think of it like a radius. Then we checking each point
250 | // in the cube and seeing if it is inside the frustum. If a point is found in front
251 | // of a side, then we skip to the next side. If we get to a plane that does NOT have
252 | // a point in front of it, then it will return false.
253 |
254 | // *Note* - This will sometimes say that a cube is inside the frustum when it isn't.
255 | // This happens when all the corners of the bounding box are not behind any one plane.
256 | // This is rare and shouldn't effect the overall rendering speed.
257 |
258 | for (int i = 0; i < 6; i++) {
259 | if (m_Frustum[i][A] * minX + m_Frustum[i][B] * minY + m_Frustum[i][C] * minZ + m_Frustum[i][D] > 0)
260 | continue;
261 | if (m_Frustum[i][A] * maxX + m_Frustum[i][B] * minY + m_Frustum[i][C] * minZ + m_Frustum[i][D] > 0)
262 | continue;
263 | if (m_Frustum[i][A] * minX + m_Frustum[i][B] * maxY + m_Frustum[i][C] * minZ + m_Frustum[i][D] > 0)
264 | continue;
265 | if (m_Frustum[i][A] * maxX + m_Frustum[i][B] * maxY + m_Frustum[i][C] * minZ + m_Frustum[i][D] > 0)
266 | continue;
267 | if (m_Frustum[i][A] * minX + m_Frustum[i][B] * minY + m_Frustum[i][C] * maxZ + m_Frustum[i][D] > 0)
268 | continue;
269 | if (m_Frustum[i][A] * maxX + m_Frustum[i][B] * minY + m_Frustum[i][C] * maxZ + m_Frustum[i][D] > 0)
270 | continue;
271 | if (m_Frustum[i][A] * minX + m_Frustum[i][B] * maxY + m_Frustum[i][C] * maxZ + m_Frustum[i][D] > 0)
272 | continue;
273 | if (m_Frustum[i][A] * maxX + m_Frustum[i][B] * maxY + m_Frustum[i][C] * maxZ + m_Frustum[i][D] > 0)
274 | continue;
275 |
276 | // If we get here, it isn't in the frustum
277 | return false;
278 | }
279 |
280 | return true;
281 | }
282 |
283 | public boolean isVisible(AABB aabb) {
284 | return cubeInFrustum((float) aabb.minX, (float) aabb.minY, (float) aabb.minZ,
285 | (float) aabb.maxX, (float) aabb.maxY, (float) aabb.maxZ);
286 | }
287 | }
--------------------------------------------------------------------------------
/src/main/java/com/mojang/minecraft/Minecraft.java:
--------------------------------------------------------------------------------
1 | package com.mojang.minecraft;
2 |
3 | import com.mojang.minecraft.character.Zombie;
4 | import com.mojang.minecraft.level.*;
5 | import com.mojang.minecraft.level.tile.Tile;
6 | import com.mojang.minecraft.particle.ParticleEngine;
7 | import org.lwjgl.BufferUtils;
8 | import org.lwjgl.LWJGLException;
9 | import org.lwjgl.input.Keyboard;
10 | import org.lwjgl.input.Mouse;
11 | import org.lwjgl.opengl.Display;
12 | import org.lwjgl.opengl.DisplayMode;
13 |
14 | import javax.swing.*;
15 | import java.awt.*;
16 | import java.nio.FloatBuffer;
17 | import java.nio.IntBuffer;
18 | import java.util.ArrayList;
19 | import java.util.Iterator;
20 | import java.util.List;
21 |
22 | import static org.lwjgl.opengl.GL11.*;
23 | import static org.lwjgl.util.glu.GLU.gluPerspective;
24 | import static org.lwjgl.util.glu.GLU.gluPickMatrix;
25 |
26 | public class Minecraft implements Runnable {
27 |
28 | private final Timer timer = new Timer(20);
29 |
30 | private Level level;
31 | private LevelRenderer levelRenderer;
32 | private Player player;
33 |
34 | private final List zombies = new ArrayList<>();
35 | private ParticleEngine particleEngine;
36 |
37 | /**
38 | * Fog
39 | */
40 | private final FloatBuffer fogColorDaylight = BufferUtils.createFloatBuffer(4);
41 | private final FloatBuffer fogColorShadow = BufferUtils.createFloatBuffer(4);
42 | private final FloatBuffer colorBuffer = BufferUtils.createFloatBuffer(16);
43 |
44 | /**
45 | * Tile picking
46 | */
47 | private final IntBuffer viewportBuffer = BufferUtils.createIntBuffer(16);
48 | private final IntBuffer selectBuffer = BufferUtils.createIntBuffer(2000);
49 | private HitResult hitResult;
50 |
51 | /**
52 | * HUD rendering
53 | */
54 | private final Tessellator tessellator = new Tessellator();
55 |
56 | /**
57 | * Selected tile in hand
58 | */
59 | private int selectedTileId = 1;
60 |
61 | /**
62 | * Canvas
63 | */
64 | private final Canvas parent;
65 | private int width;
66 | private int height;
67 | private final boolean fullscreen;
68 | public boolean appletMode;
69 |
70 | /**
71 | * Game state
72 | */
73 | public volatile boolean running;
74 |
75 | /**
76 | * Create Minecraft instance and render it on a canvas
77 | *
78 | * @param parent The canvas as render target
79 | * @param width Canvas width
80 | * @param height Canvas height
81 | * @param fullscreen Is in fullscreen
82 | */
83 | public Minecraft(Canvas parent, int width, int height, boolean fullscreen) {
84 | this.parent = parent;
85 | this.width = width;
86 | this.height = height;
87 | this.fullscreen = fullscreen;
88 | }
89 |
90 | /**
91 | * Initialize the game.
92 | * Setup display, keyboard, mouse, rendering and camera
93 | *
94 | * @throws LWJGLException Game could not be initialized
95 | */
96 | public void init() throws LWJGLException {
97 | // Write fog color for daylight
98 | this.fogColorDaylight.put(new float[]{
99 | 254 / 255.0F,
100 | 251 / 255.0F,
101 | 250 / 255.0F,
102 | 255 / 255.0F
103 | }).flip();
104 |
105 | // Write fog color for shadow
106 | this.fogColorShadow.put(new float[]{
107 | 14 / 255.0F,
108 | 11 / 255.0F,
109 | 10 / 255.0F,
110 | 255 / 255.0F
111 | }).flip();
112 |
113 | if (this.parent == null) {
114 | if (this.fullscreen) {
115 | // Set in fullscreen
116 | Display.setFullscreen(true);
117 |
118 | // Set monitor size
119 | this.width = Display.getDisplayMode().getWidth();
120 | this.height = Display.getDisplayMode().getHeight();
121 | } else {
122 | // Set defined window size
123 | Display.setDisplayMode(new DisplayMode(this.width, this.height));
124 | }
125 | } else {
126 | // Set canvas parent
127 | Display.setParent(this.parent);
128 | }
129 |
130 | // Setup I/O
131 | Display.create();
132 | Keyboard.create();
133 | Mouse.create();
134 |
135 | // Setup texture and color
136 | glEnable(GL_TEXTURE_2D);
137 | glShadeModel(GL_SMOOTH);
138 | glClearColor(0.5F, 0.8F, 1.0F, 0.0F);
139 | glClearDepth(1.0);
140 |
141 | // Setup depth
142 | glEnable(GL_DEPTH_TEST);
143 | glEnable(GL_CULL_FACE);
144 | glDepthFunc(GL_LEQUAL);
145 |
146 | // Setup alpha
147 | glEnable(GL_ALPHA_TEST);
148 | glAlphaFunc(GL_GREATER, 0.5F);
149 |
150 | // Setup camera
151 | glMatrixMode(GL_PROJECTION);
152 | glLoadIdentity();
153 | glMatrixMode(GL_MODELVIEW);
154 |
155 | // Create level and player (Has to be in main thread)
156 | this.level = new Level(256, 256, 64);
157 | this.levelRenderer = new LevelRenderer(this.level);
158 | this.player = new Player(this.level);
159 | this.particleEngine = new ParticleEngine(this.level);
160 |
161 | // Grab mouse cursor
162 | Mouse.setGrabbed(true);
163 |
164 | // Spawn some zombies
165 | for (int i = 0; i < 10; ++i) {
166 | Zombie zombie = new Zombie(this.level, 128.0F, 0.0F, 129.0F);
167 | zombie.resetPosition();
168 | this.zombies.add(zombie);
169 | }
170 | }
171 |
172 | /**
173 | * Destroy mouse, keyboard and display
174 | */
175 | public void destroy() {
176 | this.level.save();
177 |
178 | Mouse.destroy();
179 | Keyboard.destroy();
180 | Display.destroy();
181 | }
182 |
183 | /**
184 | * Main game thread
185 | * Responsible for the game loop
186 | */
187 | @Override
188 | public void run() {
189 | // Game is running
190 | this.running = true;
191 |
192 | try {
193 | // Initialize the game
194 | init();
195 | } catch (Exception e) {
196 | // Show error message dialog and stop the game
197 | JOptionPane.showMessageDialog(null, e, "Failed to start Minecraft", JOptionPane.ERROR_MESSAGE);
198 | System.exit(0);
199 | }
200 |
201 | // To keep track of framerate
202 | int frames = 0;
203 | long lastTime = System.currentTimeMillis();
204 |
205 | try {
206 | // Start the game loop
207 | while (this.running) {
208 | // On close window
209 | if (this.parent == null && Display.isCloseRequested()) {
210 | this.stop();
211 | }
212 |
213 | // Update the timer
214 | this.timer.advanceTime();
215 |
216 | // Call the tick to reach updates 20 per seconds
217 | for (int i = 0; i < this.timer.ticks; ++i) {
218 | onTick();
219 | }
220 |
221 | // Render the game
222 | render(this.timer.partialTicks);
223 |
224 | // Increase rendered frame
225 | frames++;
226 |
227 | // Loop if a second passed
228 | while (System.currentTimeMillis() >= lastTime + 1000L) {
229 | // Print amount of frames
230 | System.out.println(frames + " fps, " + Chunk.updates);
231 |
232 | // Reset global rebuild stats
233 | Chunk.updates = 0;
234 |
235 | // Increase last time printed and reset frame counter
236 | lastTime += 1000L;
237 | frames = 0;
238 | }
239 | }
240 | } catch (Exception e) {
241 | e.printStackTrace();
242 | } finally {
243 | // Destroy I/O and save game
244 | destroy();
245 | }
246 | }
247 |
248 | /**
249 | * Stop the game
250 | */
251 | public void stop() {
252 | this.running = false;
253 | }
254 |
255 | /**
256 | * Game tick, called exactly 20 times per second
257 | */
258 | private void onTick() {
259 | // Listen for keyboard inputs
260 | while (Keyboard.next()) {
261 | if (Keyboard.getEventKeyState()) {
262 |
263 | // Exit the game
264 | if (Keyboard.getEventKey() == 1) { // Escape
265 | stop();
266 | }
267 |
268 | // Save the level
269 | if (Keyboard.getEventKey() == 28) { // Enter
270 | this.level.save();
271 | }
272 |
273 | // Tile selection
274 | if (Keyboard.getEventKey() == 2) { // 1
275 | this.selectedTileId = Tile.rock.id;
276 | }
277 | if (Keyboard.getEventKey() == 3) { // 2
278 | this.selectedTileId = Tile.dirt.id;
279 | }
280 | if (Keyboard.getEventKey() == 4) { // 3
281 | this.selectedTileId = Tile.stoneBrick.id;
282 | }
283 | if (Keyboard.getEventKey() == 5) { // 4
284 | this.selectedTileId = Tile.wood.id;
285 | }
286 | if (Keyboard.getEventKey() == 7) { // 6
287 | this.selectedTileId = Tile.bush.id;
288 | }
289 |
290 | // Spawn zombie
291 | if (Keyboard.getEventKey() == 34) { // G
292 | this.zombies.add(new Zombie(this.level, this.player.x, this.player.y, this.player.z));
293 | }
294 | }
295 | }
296 |
297 | // Tick random tile in level
298 | this.level.onTick();
299 |
300 | // Tick particles
301 | this.particleEngine.onTick();
302 |
303 | // Tick zombies
304 | Iterator iterator = this.zombies.iterator();
305 | while (iterator.hasNext()) {
306 | Zombie zombie = iterator.next();
307 |
308 | // Tick zombie
309 | zombie.onTick();
310 |
311 | // Remove zombie
312 | if (zombie.removed) {
313 | iterator.remove();
314 | }
315 | }
316 |
317 | // Tick player
318 | this.player.onTick();
319 | }
320 |
321 | /**
322 | * Move and rotate the camera to players location and rotation
323 | *
324 | * @param partialTicks Overflow ticks to interpolate
325 | */
326 | private void moveCameraToPlayer(float partialTicks) {
327 | Entity player = this.player;
328 |
329 | // Eye height
330 | glTranslatef(0.0f, 0.0f, -0.3f);
331 |
332 | // Rotate camera
333 | glRotatef(player.xRotation, 1.0f, 0.0f, 0.0f);
334 | glRotatef(player.yRotation, 0.0f, 1.0f, 0.0f);
335 |
336 | // Smooth movement
337 | double x = this.player.prevX + (this.player.x - this.player.prevX) * partialTicks;
338 | double y = this.player.prevY + (this.player.y - this.player.prevY) * partialTicks;
339 | double z = this.player.prevZ + (this.player.z - this.player.prevZ) * partialTicks;
340 |
341 | // Move camera to players location
342 | glTranslated(-x, -y, -z);
343 | }
344 |
345 |
346 | /**
347 | * Setup the normal player camera
348 | *
349 | * @param partialTicks Overflow ticks to interpolate
350 | */
351 | private void setupCamera(float partialTicks) {
352 | glMatrixMode(GL_PROJECTION);
353 | glLoadIdentity();
354 |
355 | // Set camera perspective
356 | gluPerspective(70, width / (float) height, 0.05F, 1000F);
357 |
358 | glMatrixMode(GL_MODELVIEW);
359 | glLoadIdentity();
360 |
361 | // Move camera to middle of level
362 | moveCameraToPlayer(partialTicks);
363 | }
364 |
365 | /**
366 | * Setup tile picking camera
367 | *
368 | * @param partialTicks Overflow ticks to calculate smooth a movement
369 | * @param x Screen position x
370 | * @param y Screen position y
371 | */
372 | private void setupPickCamera(float partialTicks, int x, int y) {
373 | glMatrixMode(GL_PROJECTION);
374 | glLoadIdentity();
375 |
376 | // Reset buffer
377 | this.viewportBuffer.clear();
378 |
379 | // Get viewport value
380 | glGetInteger(GL_VIEWPORT, this.viewportBuffer);
381 |
382 | // Flip
383 | this.viewportBuffer.flip();
384 | this.viewportBuffer.limit(16);
385 |
386 | // Set matrix and camera perspective
387 | gluPickMatrix(x, y, 5.0F, 5.0F, this.viewportBuffer);
388 | gluPerspective(70.0F, this.width / (float) this.height, 0.05F, 1000.0F);
389 |
390 | glMatrixMode(GL_MODELVIEW);
391 | glLoadIdentity();
392 |
393 | // Move camera to middle of level
394 | moveCameraToPlayer(partialTicks);
395 | }
396 |
397 | /**
398 | * @param partialTicks Overflow ticks to interpolate
399 | */
400 | private void pick(float partialTicks) {
401 | // Reset select buffer
402 | this.selectBuffer.clear();
403 |
404 | glSelectBuffer(this.selectBuffer);
405 | glRenderMode(GL_SELECT);
406 |
407 | // Setup pick camera
408 | this.setupPickCamera(partialTicks, this.width / 2, this.height / 2);
409 |
410 | // Render all possible pick selection faces to the target
411 | this.levelRenderer.pick(this.player, Frustum.getFrustum());
412 |
413 | // Flip buffer
414 | this.selectBuffer.flip();
415 | this.selectBuffer.limit(this.selectBuffer.capacity());
416 |
417 | long closest = 0L;
418 | int[] names = new int[10];
419 | int hitNameCount = 0;
420 |
421 | // Get amount of hits
422 | int hits = glRenderMode(GL_RENDER);
423 | for (int hitIndex = 0; hitIndex < hits; hitIndex++) {
424 |
425 | // Get name count
426 | int nameCount = this.selectBuffer.get();
427 | long minZ = this.selectBuffer.get();
428 | this.selectBuffer.get();
429 |
430 | // Check if the hit is closer to the camera
431 | if (minZ < closest || hitIndex == 0) {
432 | closest = minZ;
433 | hitNameCount = nameCount;
434 |
435 | // Fill names
436 | for (int nameIndex = 0; nameIndex < nameCount; nameIndex++) {
437 | names[nameIndex] = this.selectBuffer.get();
438 | }
439 | } else {
440 | // Skip names
441 | for (int nameIndex = 0; nameIndex < nameCount; ++nameIndex) {
442 | this.selectBuffer.get();
443 | }
444 | }
445 | }
446 |
447 | // Update hit result
448 | if (hitNameCount > 0) {
449 | this.hitResult = new HitResult(names[0], names[1], names[2], names[3], names[4]);
450 | } else {
451 | this.hitResult = null;
452 | }
453 | }
454 |
455 |
456 | /**
457 | * Rendering the game
458 | *
459 | * @param partialTicks Overflow ticks to interpolate
460 | */
461 | private void render(float partialTicks) {
462 | // Get mouse motion
463 | float motionX = Mouse.getDX();
464 | float motionY = Mouse.getDY();
465 |
466 | // Rotate the camera using the mouse motion input
467 | this.player.turn(motionX, motionY);
468 |
469 | // Pick tile
470 | pick(partialTicks);
471 |
472 | // Listen for mouse inputs
473 | while (Mouse.next()) {
474 | // Right click
475 | if (Mouse.getEventButton() == 1 && Mouse.getEventButtonState() && this.hitResult != null) {
476 | Tile previousTile = Tile.tiles[this.level.getTile(this.hitResult.x, this.hitResult.y, this.hitResult.z)];
477 |
478 | // Destroy the tile
479 | boolean tileChanged = this.level.setTile(this.hitResult.x, this.hitResult.y, this.hitResult.z, 0);
480 |
481 | // Create particles for this tile
482 | if (previousTile != null && tileChanged) {
483 | previousTile.onDestroy(this.level, this.hitResult.x, this.hitResult.y, this.hitResult.z, this.particleEngine);
484 | }
485 | }
486 |
487 | // Left click
488 | if (Mouse.getEventButton() == 0 && Mouse.getEventButtonState() && this.hitResult != null) {
489 | // Get target tile position
490 | int x = this.hitResult.x;
491 | int y = this.hitResult.y;
492 | int z = this.hitResult.z;
493 |
494 | // Get position of the tile using face direction
495 | if (this.hitResult.face == 0) y--;
496 | if (this.hitResult.face == 1) y++;
497 | if (this.hitResult.face == 2) z--;
498 | if (this.hitResult.face == 3) z++;
499 | if (this.hitResult.face == 4) x--;
500 | if (this.hitResult.face == 5) x++;
501 |
502 | // Set the tile
503 | this.level.setTile(x, y, z, this.selectedTileId);
504 | }
505 | }
506 |
507 | // Clear color and depth buffer and reset the camera
508 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
509 |
510 | // Setup normal player camera
511 | setupCamera(partialTicks);
512 | glEnable(GL_CULL_FACE);
513 |
514 | // Get current frustum
515 | Frustum frustum = Frustum.getFrustum();
516 |
517 | // Update dirty chunks
518 | this.levelRenderer.updateDirtyChunks(this.player);
519 |
520 | // Setup daylight fog
521 | setupFog(0);
522 | glEnable(GL_FOG);
523 |
524 | // Render bright tiles
525 | this.levelRenderer.render(0);
526 |
527 | // Render zombies in sunlight
528 | for (Zombie zombie : this.zombies) {
529 | if (zombie.isLit() && frustum.isVisible(zombie.boundingBox)) {
530 | zombie.render(partialTicks);
531 | }
532 | }
533 |
534 | // Render particles in sunlight
535 | this.particleEngine.render(this.player, this.tessellator, partialTicks, 0);
536 |
537 | // Setup shadow fog
538 | setupFog(1);
539 |
540 | // Render dark tiles in shadow
541 | this.levelRenderer.render(1);
542 |
543 | // Render zombies in shadow
544 | for (Zombie zombie : this.zombies) {
545 | if (!zombie.isLit() && frustum.isVisible(zombie.boundingBox)) {
546 | zombie.render(partialTicks);
547 | }
548 | }
549 |
550 | // Render particles in shadow
551 | this.particleEngine.render(this.player, this.tessellator, partialTicks, 1);
552 |
553 | // Finish rendering
554 | glDisable(GL_LIGHTING);
555 | glDisable(GL_TEXTURE_2D);
556 | glDisable(GL_FOG);
557 |
558 | // Render the actual hit
559 | if (this.hitResult != null) {
560 | glDisable(GL_ALPHA_TEST);
561 | this.levelRenderer.renderHit(this.hitResult);
562 | glEnable(GL_ALPHA_TEST);
563 | }
564 |
565 | // Draw player HUD
566 | drawGui(partialTicks);
567 |
568 | // Update the display
569 | Display.update();
570 | }
571 |
572 | /**
573 | * Draw HUD
574 | *
575 | * @param partialTicks Overflow ticks to interpolate
576 | */
577 | private void drawGui(float partialTicks) {
578 | // Clear depth
579 | glClear(GL_DEPTH_BUFFER_BIT);
580 |
581 | // Setup HUD camera
582 | glMatrixMode(GL_PROJECTION);
583 | glLoadIdentity();
584 |
585 | int screenWidth = this.width * 240 / this.height;
586 | int screenHeight = this.height * 240 / this.height;
587 |
588 | // Set camera perspective
589 | glOrtho(0.0, screenWidth, screenHeight, 0.0, 100.0F, 300.0F);
590 |
591 | glMatrixMode(GL_MODELVIEW);
592 | glLoadIdentity();
593 |
594 | // Move camera to Z level -200
595 | glTranslatef(0.0f, 0.0f, -200.0f);
596 |
597 | // Start tile display
598 | glPushMatrix();
599 |
600 | // Transform tile position to the top right corner
601 | glTranslated(screenWidth - 16, 16.0F, 0.0F);
602 | glScalef(16.0F, 16.0F, 16.0F);
603 | glRotatef(30.0F, 1.0F, 0.0F, 0.0F);
604 | glRotatef(45.0F, 0.0F, 1.0F, 0.0F);
605 | glTranslatef(-1.5F, 0.5F, -0.5F);
606 | glScalef(-1.0F, -1.0F, 1.0F);
607 |
608 | // Setup tile rendering
609 | int id = Textures.loadTexture("/terrain.png", 9728);
610 | glBindTexture(GL_TEXTURE_2D, id);
611 | glEnable(GL_TEXTURE_2D);
612 |
613 | // Render selected tile in hand
614 | this.tessellator.init();
615 | Tile.tiles[this.selectedTileId].render(this.tessellator, this.level, 0, -2, 0, 0);
616 | this.tessellator.flush();
617 |
618 | // Finish tile rendering
619 | glDisable(GL_TEXTURE_2D);
620 | glPopMatrix();
621 |
622 | // Cross hair position
623 | int x = screenWidth / 2;
624 | int y = screenHeight / 2;
625 |
626 | // Cross hair color
627 | glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
628 |
629 | // Render cross hair
630 | this.tessellator.init();
631 | this.tessellator.vertex((float) (x + 1), (float) (y - 4), 0.0F);
632 | this.tessellator.vertex((float) (x - 0), (float) (y - 4), 0.0F);
633 | this.tessellator.vertex((float) (x - 0), (float) (y + 5), 0.0F);
634 | this.tessellator.vertex((float) (x + 1), (float) (y + 5), 0.0F);
635 | this.tessellator.vertex((float) (x + 5), (float) (y - 0), 0.0F);
636 | this.tessellator.vertex((float) (x - 4), (float) (y - 0), 0.0F);
637 | this.tessellator.vertex((float) (x - 4), (float) (y + 1), 0.0F);
638 | this.tessellator.vertex((float) (x + 5), (float) (y + 1), 0.0F);
639 | this.tessellator.flush();
640 | }
641 |
642 | /**
643 | * Setup fog with type
644 | *
645 | * @param fogType Type of the fog. (0: daylight, 1: shadow)
646 | */
647 | private void setupFog(int fogType) {
648 | // Daylight fog
649 | if (fogType == 0) {
650 | // Fog distance
651 | glFogi(GL_FOG_MODE, GL_VIEWPORT_BIT);
652 | glFogf(GL_FOG_DENSITY, 0.001F);
653 |
654 | // Set fog color
655 | glFog(GL_FOG_COLOR, this.fogColorDaylight);
656 |
657 | glDisable(GL_LIGHTING);
658 | }
659 |
660 | // Shadow fog
661 | if (fogType == 1) {
662 | // Fog distance
663 | glFogi(GL_FOG_MODE, GL_VIEWPORT_BIT);
664 | glFogf(GL_FOG_DENSITY, 0.06F);
665 |
666 | // Set fog color
667 | glFog(GL_FOG_COLOR, this.fogColorShadow);
668 |
669 | glEnable(GL_LIGHTING);
670 | glEnable(GL_COLOR_MATERIAL);
671 |
672 | float brightness = 0.6F;
673 | glLightModel(GL_LIGHT_MODEL_AMBIENT, this.getBuffer(brightness, brightness, brightness, 1.0F));
674 | }
675 | }
676 |
677 | /**
678 | * Fill float buffer with color values and return it
679 | *
680 | * @param red Red value
681 | * @param green Green value
682 | * @param blue Blue value
683 | * @param alpha Alpha value
684 | * @return Float buffer filled in RGBA order
685 | */
686 | private FloatBuffer getBuffer(float red, float green, float blue, float alpha) {
687 | this.colorBuffer.clear();
688 | this.colorBuffer.put(red).put(green).put(blue).put(alpha);
689 | this.colorBuffer.flip();
690 | return this.colorBuffer;
691 | }
692 |
693 | /**
694 | * Entry point of the game
695 | *
696 | * @param args Program arguments (unused)
697 | */
698 | public static void main(String[] args) {
699 | boolean fullScreen = false;
700 |
701 | // Find fullscreen argument
702 | for (String arg : args) {
703 | if (arg.equalsIgnoreCase("-fullscreen")) {
704 | fullScreen = true;
705 | break;
706 | }
707 | }
708 |
709 | // Launch
710 | new Thread(new Minecraft(null, 1024, 768, fullScreen)).start();
711 | }
712 |
713 | }
714 |
--------------------------------------------------------------------------------