Craft

使用现代 OpenGL(着色器)以 C 语言编写的简单 Minecraft 克隆。「A simple Minecraft clone written in C using modern OpenGL (shaders).」

Github星跟蹤圖

Craft

适用于 Windows、Mac OS X 和 Linux 的 Minecraft 克隆。使用现代 OpenGL(着色器)仅几千行 C。使用基于 Python 的服务器提供在线多人游戏支持。

http://www.michaelfogleman.com/craft/

Screenshot

特性

  • 使用 perlin/simplex noise 生成简单但美观的地形。
  • 可以轻松添加十多种类型的块,甚至更多。
  • 支持植物(草,花,树等)和透明度(玻璃)。
  • 天空中只有简单的云(它们不会移动)。
  • 日/夜循环和和一个纹理天空穹顶。
  • 世界变化持久化在 sqlite3 数据库中。
  • 支持多人游戏!

下载

网站上提供了 Mac 和 Windows 二进制文件。

http://www.michaelfogleman.com/craft/

参见下文,从源代码运行。

安装依赖项

Mac OS X

如果尚未安装,请下载并安装 CMake。您可以使用 Homebrew 简化安装:

Linux (Ubuntu)

sudo apt-get install cmake libglew-dev xorg-dev libcurl4-openssl-dev
sudo apt-get build-dep glfw

Windows

下载并安装 CMake 和 MinGW。 将 C:\MinGW\bin 添加到 PATH。

下载并安装 cURL,以便 CURL/lib 和 CURL/include 位于“程序文件”目录中。

使用以下命令代替下一部分中描述的命令。

cmake -G "MinGW Makefiles"
mingw32-make

编译并运行

一旦有了依赖项(见上文),请在终端中运行以下命令。

git clone https://github.com/fogleman/Craft.git
cd Craft
cmake .
make
./craft

多人游戏

注册一个帐户!

https://craft.michaelfogleman.com/

客户端

您可以使用命令行参数连接到服务器...

./craft craft.michaelfogleman.com

或者,使用游戏本身中的“/online”命令。

/online craft.michaelfogleman.com

服务器

您可以运行自己的服务器或连接到我的服务器。 服务器是用 Python 编写的,但需要一个已编译的 DLL,因此它可以像客户端一样执行地形生成。

gcc -std=c99 -O3 -fPIC -shared -o world -I src -I deps/noise deps/noise/noise.c src/world.c
python server.py [HOST [PORT]]

控制项

  • WASD 向前,向左,向后,向右移动。
  • 空格跳跃。
  • 左键单击以破坏方块。
  • 右击或 Cmd + 左击创建一个块。
  • Ctrl + 右键单击以将块切换为光源。
  • 1-9选择要创建的块类型。
  • E 循环浏览块类型。
  • Tab 可在步行和飞行之间切换。
  • ZXCVBN 沿 XYZ 轴沿精确方向移动。
  • 左 shift 缩放。
  • F 以正交模式显示场景。
  • O 在主视图中观察玩家。
  • P 以画中画方式观察玩家。
  • T 将文本输入聊天。
  • 正斜杠(/)输入命令。
  • 反引号(`),可在任何块(符号)上写文本。
  • 箭头键模拟鼠标移动。
  • Enter模拟鼠标单击。

聊天命令

/goto [NAME]

传送到另一个用户。如果未指定NAME,则选择一个随机用户。

/list

显示已连接用户的列表。

/login NAME

切换到另一个注册的用户名。将重新联系登录服务器。用户名区分大小写。

/logout

取消身份验证并成为访客用户。在重新发出 /login 命令之前,不会再次发生自动登录。

/offline [FILE]

切换到离线模式。 FILE指定要使用的保存文件,默认为“craft”。

/online HOST [PORT]

连接到指定的服务器。

/pq P Q

传送到指定的块。

/spawn

传送回生成点。

屏幕截图

Screenshot

实施细节

地形生成

地形是使用“单纯形”噪声生成的,“单纯形”噪声是根据位置播种的确定性噪声函数。因此,在给定位置始终将以相同方式生成世界。

在 XZ 平面中,世界被分成 32x32 的块块(Y向上)。这样可以使世界变得“无限”(在 X 或 Z 值较大时,浮点精度目前是一个问题),还可以更轻松地管理数据。只需要从数据库中查询可见的块。

渲染图

仅渲染暴露的面。这是一项重要的优化,因为绝大多数图块要么完全被隐藏,要么仅暴露一个或两个面。每个块记录每个相邻块的一个块的宽度重叠,因此它知道沿其周边暴露了哪些块。

仅显示可见的块。天真的视锥剔除方法用于测试相机视图中是否有块。如果不是,则不渲染。这也导致相当不错的性能改进。

当块中的块发生更改时,块缓冲区将完全重新生成,而不是尝试更新 VBO。

使用位图地图集呈现文本。每个字符都渲染到形成 2D 矩形的两个三角形上。

使用“现代”OpenGL -- 不使用不推荐使用的固定功能管线功能。顶点缓冲对象用于位置,法线和纹理坐标。顶点和片段着色器用于渲染。矩阵操纵函数位于 matrix.c 中,用于平移,旋转,透视,正射等矩阵。 3D模型由非常简单的图元组成 -- 主要是立方体和矩形。这些模型是在 cube.c 中的代码中生成的。

通过丢弃片段着色器中的洋红色像素,可以实现玻璃块和植物(植物不占据其三角形基元的完整矩形形状)的透明度。

数据库

用户对世界的更改存储在 sqlite 数据库中。仅存储增量,因此将生成默认世界,然后在加载时将用户更改应用到顶部。

主数据库表名为“块”,并具有列 p,q,x,y,z,w。 (p,q)标识块,(x,y,z)标识块位置,(w)标识块类型。 0 表示一个空块(空气)。

在游戏中,块将其块存储在哈希图中。(x,y,z)键映射到(w)值。

块的 y 位置限制为 0 <= y <256。上限主要是人为的限制,以防止用户建造不必要的高大结构。禁止用户破坏 y = 0 的块,以免掉入世界。

多人游戏

多人游戏模式是使用普通的套接字实现的。使用了一个简单的基于行的 ASCII 协议。每行由一个命令代码和零个或多个逗号分隔的参数组成。客户端使用一个简单的命令从服务器请求块:C,p,q,key。 “ C”表示“块”,(p,q)标识块。该密钥用于缓存-服务器将仅发送自客户端最后一次请求该块以来执行的块更新。块更新(实时或作为块请求的一部分)以 B,p,q,x,y,z,w 的格式发送到客户端。发送请求的块的所有块后,服务器将以以下格式发送更新的缓存键:K,p,q,key。客户端将存储此密钥,并在下次需要该块时使用它。玩家位置以以下格式发送:P,pid,x,y,z,rx,ry。 pid 是玩家 ID,rx 和 ry 值表示玩家在两个不同轴上的旋转。客户端从过去两个位置更新中插入玩家位置,以使动画更流畅。客户端最多每 0.1 秒将其位置发送到服务器(如果不移动则更少)。

首次连接到服务器时,客户端缓存到 sqlite 数据库可能会占用大量性能。因此,在后台线程上执行 sqlite 写操作。所有写入都发生在事务中以提高性能。每5秒钟提交一次事务,而不是逻辑上完成一些工作。环形/循环缓冲区用作将哪些数据写入数据库的队列。

在多人游戏模式下,玩家可以在主视图或画中画视图中彼此观察。 PnP 的实现非常简单 -- 只需更改视口并从其他玩家的角度重新渲染场景即可。

碰撞测试

命中测试(用户指向的障碍物)是通过按照玩家的视线从玩家的位置向外扫描光线来实现的。这不是一种精确的方法,因此可以使步速变得更小而更加精确。

碰撞测试只是简单地调整玩家的位置,使其与障碍物相邻的障碍物保持一定距离。 (云和植物未标记为障碍物,因此您可以直通它们。)

天穹

一个织地不很细的天空圆顶用于天空。 纹理的 X 坐标表示一天中的时间。 Y 值从天空球的底部到天空球的顶部映射。玩家始终位于球体的中心。块的片段着色器还会对天空纹理进行采样,以根据块相对于背景天空的位置来确定要混合的合适雾色。

环境光遮蔽

环境遮挡的实现方法如下页所述:
http://0fps.wordpress.com/2013/07/03/ambient-occlu...

依存关系

  • GLEW 用于管理跨平台的 OpenGL 扩展。
  • GLFW 用于跨平台窗口管理。
  • CURL 用于 HTTPS/SSL POST 的身份验证过程。
  • lodepng 用于加载 PNG 纹理。
  • sqlite3 用于保存用户添加/删除的块。
  • tinycthread 用于跨平台线程。


(The first version translated by vz on 2020.08.08)

主要指標

概覽
名稱與所有者fogleman/Craft
主編程語言C
編程語言Python (語言數: 4)
平台Linux, Mac, Windows
許可證MIT License
所有者活动
創建於2013-04-09 19:47:38
推送於2024-04-03 15:49:34
最后一次提交2021-02-24 10:40:38
發布數1
最新版本名稱v1.0 (發布於 )
第一版名稱v1.0 (發布於 )
用户参与
星數10.8k
關注者數352
派生數1.4k
提交數753
已啟用問題?
問題數161
打開的問題數79
拉請求數27
打開的拉請求數46
關閉的拉請求數80
项目设置
已啟用Wiki?
已存檔?
是復刻?
已鎖定?
是鏡像?
是私有?

Craft

Minecraft clone for Windows, Mac OS X and Linux. Just a few thousand lines of C using modern OpenGL (shaders). Online multiplayer support is included using a Python-based server.

http://www.michaelfogleman.com/craft/

Screenshot

Features

  • Simple but nice looking terrain generation using perlin / simplex noise.
  • More than 10 types of blocks and more can be added easily.
  • Supports plants (grass, flowers, trees, etc.) and transparency (glass).
  • Simple clouds in the sky (they don't move).
  • Day / night cycles and a textured sky dome.
  • World changes persisted in a sqlite3 database.
  • Multiplayer support!

Download

Mac and Windows binaries are available on the website.

http://www.michaelfogleman.com/craft/

See below to run from source.

Install Dependencies

Mac OS X

Download and install CMake
if you don't already have it. You may use Homebrew to simplify
the installation:

brew install cmake

Linux (Ubuntu)

sudo apt-get install cmake libglew-dev xorg-dev libcurl4-openssl-dev
sudo apt-get build-dep glfw

Windows

Download and install CMake
and MinGW. Add C:\MinGW\bin to your PATH.

Download and install cURL so that
CURL/lib and CURL/include are in your Program Files directory.

Use the following commands in place of the ones described in the next section.

cmake -G "MinGW Makefiles"
mingw32-make

Compile and Run

Once you have the dependencies (see above), run the following commands in your
terminal.

git clone https://github.com/fogleman/Craft.git
cd Craft
cmake .
make
./craft

Multiplayer

Register for an account!

https://craft.michaelfogleman.com/

Client

You can connect to a server with command line arguments...

./craft craft.michaelfogleman.com

Or, with the "/online" command in the game itself.

/online craft.michaelfogleman.com

Server

You can run your own server or connect to mine. The server is written in Python
but requires a compiled DLL so it can perform the terrain generation just like
the client.

gcc -std=c99 -O3 -fPIC -shared -o world -I src -I deps/noise deps/noise/noise.c src/world.c
python server.py [HOST [PORT]]

Controls

  • WASD to move forward, left, backward, right.
  • Space to jump.
  • Left Click to destroy a block.
  • Right Click or Cmd + Left Click to create a block.
  • Ctrl + Right Click to toggle a block as a light source.
  • 1-9 to select the block type to create.
  • E to cycle through the block types.
  • Tab to toggle between walking and flying.
  • ZXCVBN to move in exact directions along the XYZ axes.
  • Left shift to zoom.
  • F to show the scene in orthographic mode.
  • O to observe players in the main view.
  • P to observe players in the picture-in-picture view.
  • T to type text into chat.
  • Forward slash (/) to enter a command.
  • Backquote (`) to write text on any block (signs).
  • Arrow keys emulate mouse movement.
  • Enter emulates mouse click.

Chat Commands

/goto [NAME]

Teleport to another user.
If NAME is unspecified, a random user is chosen.

/list

Display a list of connected users.

/login NAME

Switch to another registered username.
The login server will be re-contacted. The username is case-sensitive.

/logout

Unauthenticate and become a guest user.
Automatic logins will not occur again until the /login command is re-issued.

/offline [FILE]

Switch to offline mode.
FILE specifies the save file to use and defaults to "craft".

/online HOST [PORT]

Connect to the specified server.

/pq P Q

Teleport to the specified chunk.

/spawn

Teleport back to the spawn point.

Screenshot

Screenshot

Implementation Details

Terrain Generation

The terrain is generated using Simplex noise - a deterministic noise function seeded based on position. So the world will always be generated the same way in a given location.

The world is split up into 32x32 block chunks in the XZ plane (Y is up). This allows the world to be “infinite” (floating point precision is currently a problem at large X or Z values) and also makes it easier to manage the data. Only visible chunks need to be queried from the database.

Rendering

Only exposed faces are rendered. This is an important optimization as the vast majority of blocks are either completely hidden or are only exposing one or two faces. Each chunk records a one-block width overlap for each neighboring chunk so it knows which blocks along its perimeter are exposed.

Only visible chunks are rendered. A naive frustum-culling approach is used to test if a chunk is in the camera’s view. If it is not, it is not rendered. This results in a pretty decent performance improvement as well.

Chunk buffers are completely regenerated when a block is changed in that chunk, instead of trying to update the VBO.

Text is rendered using a bitmap atlas. Each character is rendered onto two triangles forming a 2D rectangle.

“Modern” OpenGL is used - no deprecated, fixed-function pipeline functions are used. Vertex buffer objects are used for position, normal and texture coordinates. Vertex and fragment shaders are used for rendering. Matrix manipulation functions are in matrix.c for translation, rotation, perspective, orthographic, etc. matrices. The 3D models are made up of very simple primitives - mostly cubes and rectangles. These models are generated in code in cube.c.

Transparency in glass blocks and plants (plants don’t take up the full rectangular shape of their triangle primitives) is implemented by discarding magenta-colored pixels in the fragment shader.

Database

User changes to the world are stored in a sqlite database. Only the delta is stored, so the default world is generated and then the user changes are applied on top when loading.

The main database table is named “block” and has columns p, q, x, y, z, w. (p, q) identifies the chunk, (x, y, z) identifies the block position and (w) identifies the block type. 0 represents an empty block (air).

In game, the chunks store their blocks in a hash map. An (x, y, z) key maps to a (w) value.

The y-position of blocks are limited to 0 <= y < 256. The upper limit is mainly an artificial limitation to prevent users from building unnecessarily tall structures. Users are not allowed to destroy blocks at y = 0 to avoid falling underneath the world.

Multiplayer

Multiplayer mode is implemented using plain-old sockets. A simple, ASCII, line-based protocol is used. Each line is made up of a command code and zero or more comma-separated arguments. The client requests chunks from the server with a simple command: C,p,q,key. “C” means “Chunk” and (p, q) identifies the chunk. The key is used for caching - the server will only send block updates that have been performed since the client last asked for that chunk. Block updates (in realtime or as part of a chunk request) are sent to the client in the format: B,p,q,x,y,z,w. After sending all of the blocks for a requested chunk, the server will send an updated cache key in the format: K,p,q,key. The client will store this key and use it the next time it needs to ask for that chunk. Player positions are sent in the format: P,pid,x,y,z,rx,ry. The pid is the player ID and the rx and ry values indicate the player’s rotation in two different axes. The client interpolates player positions from the past two position updates for smoother animation. The client sends its position to the server at most every 0.1 seconds (less if not moving).

Client-side caching to the sqlite database can be performance intensive when connecting to a server for the first time. For this reason, sqlite writes are performed on a background thread. All writes occur in a transaction for performance. The transaction is committed every 5 seconds as opposed to some logical amount of work completed. A ring / circular buffer is used as a queue for what data is to be written to the database.

In multiplayer mode, players can observe one another in the main view or in a picture-in-picture view. Implementation of the PnP was surprisingly simple - just change the viewport and render the scene again from the other player’s point of view.

Collision Testing

Hit testing (what block the user is pointing at) is implemented by scanning a ray from the player’s position outward, following their sight vector. This is not a precise method, so the step rate can be made smaller to be more accurate.

Collision testing simply adjusts the player’s position to remain a certain distance away from any adjacent blocks that are obstacles. (Clouds and plants are not marked as obstacles, so you pass right through them.)

Sky Dome

A textured sky dome is used for the sky. The X-coordinate of the texture represents time of day. The Y-values map from the bottom of the sky sphere to the top of the sky sphere. The player is always in the center of the sphere. The fragment shaders for the blocks also sample the sky texture to determine the appropriate fog color to blend with based on the block’s position relative to the backing sky.

Ambient Occlusion

Ambient occlusion is implemented as described on this page:

http://0fps.wordpress.com/2013/07/03/ambient-occlusion-for-minecraft-like-worlds/

Dependencies

  • GLEW is used for managing OpenGL extensions across platforms.
  • GLFW is used for cross-platform window management.
  • CURL is used for HTTPS / SSL POST for the authentication process.
  • lodepng is used for loading PNG textures.
  • sqlite3 is used for saving the blocks added / removed by the user.
  • tinycthread is used for cross-platform threading.