在自己瞎搞地图画生成器之后去下载别人的生成器看了看,能输出地图文件、投影(一个Mod,但我没用过,只知道大体功能)文件,反正感觉没法直接搞到服务器里面去。于是想了点别的路子。
开始只是想编个脚本就解决的,结果发现很难搞。那个会单开一个文说说。最终我准备编一个Mod来实现,关于Mod开发我也会单开一个文(虽然我也是刚刚接触)。
首先是得搞一个平台,当作背景。随便一个地方,新生成一个地图,确定好边界,搞一个128*128方块的平台即可。
这个不太难实现,先确定一个地图的顶点,再向X轴、Z轴延伸。

这里有两个点得说一下:一个是我现在知道的Mod里放置方块的方法(useItemOn)不能在玩家一定格数之外放置方块(就和手动放置一样),所以得让玩家跟着动起来。另一个是,在服务器里moveTo方法不能在1Tick里移动超过一定格数,所以没法光两个for循环直接套。需要加点东西,让玩家蛇形走位。
大体思路就是这样:确定基点>>{放置方块>>蛇形走位>>}*n(下面的SetAndRun()方法)
再加上暂停、自动切换物品栏等功能,就比较完善了。虽然老是莫名崩溃😅
平台搭完,就该“作画”了,想不出什么太好的方法,就是通过Mod外面的(下面会有)程序,把图片(128*128像素)转换成0~9,储存到文档里。
Mod到时直接读取文档,文档内的0~8对应着游戏内1~9的物品栏,9对应空气(啥都不放),Mod会自动跳过去。
大体的思路:建立两个数组,一个储存第几个格子选择哪个物品栏(放什么方块)。另一个储存所有要放置方块的格子的格子数(第几个格子)。
第一个数组是给切换物品栏使用的,让Mod一次可以搭好几种方块,对应着下面的ChooseAndSet()方法。
第二个数组是给玩家移动用的,让玩家只走到要搭方块的地方,而不是128*128全逛一遍,节约时间,对应的是下面的WantGo()方法。

下面就是代码了,前面是生成地图文档的java程序,后面是Mod的程序。
这会试了试,现在的代码在这里就能读了,不用再复制到IDE里了😉
package pixelpaint;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import javax.imageio.ImageIO;
public class Word2Txt {
//路径:D:/CC-JAVA/IO,自己用时记得改。
public static void main(String[] argv) throws IOException {
java.awt.image.BufferedImage img = ImageIO.read(new File("D:/CC-JAVA/IO/in.jpg"));
File newfiFile = new File("D:/CC-JAVA/IO/file.txt");
FileOutputStream fileOutputStream;
int width = img.getWidth();
int height = img.getHeight();
int q = 1;
int max = 0;
try {
fileOutputStream = new FileOutputStream(newfiFile);
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream(newfiFile));
BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
for (int y = 0; y < height; y += q) {
for (int x = 0; x < width; x += q) {
int r = img.getRGB(x, y);
int red = (r >> 16) & 0x0ff;
int green = (r >> 8) & 0x0ff;
int blue = r & 0x0ff;
max = Math.max(red, Math.max(green, blue));
if (max < 178) {
bufferedWriter.write("8");
bufferedWriter.write("\n");
} el se {
bufferedWriter.write("9");
bufferedWriter.write("\n");
}
}
}
bufferedWriter.close();
outputStreamWriter.close();
fileOutputStream.close();
System.err.println("ok");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
发现有的代码太长了,还是搞到IDE里好看点😐
也可以来https://github.com/C20C01/AutoBuild_McMod 康康
这个是使用的是官方的映射表,而不是MCP映射表,所以如果你使用MCP映射表,大部分方法的叫法应该都不一样,得自己改咯。
package com.example.examplemod;
import net.minecraft.client.Minecraft;
import net.minecraft.item.Items;
import net.minecraft.util.Direction;
import net.minecraft.util.Hand;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvents;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.util.text.StringTextComponent;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import java.io.*;
import java.util.Timer;
import java.util.TimerTask;
@Mod(CCMod.MODID)
@Mod.EventBusSubscriber(value = Dist.CLIENT, modid = CCMod.MODID)
public class CCMod {
static final String MODID = "autobuild";
private static final Minecraft mc = Minecraft.getInstance();
private static boolean attackDown = false;
private static boolean doingJob = false;
private static Timer timer;
private static boolean first = true;
private static boolean set = false;
private static BlockPos firstpoint = null;
private static int base_x, base_y, base_z;
private static int x = 0;
private static int z = 0;
private static int longX = 8;
private static int longZ = 8;
private static boolean positive = true;
private static int number = 0;
private static final int[] BLOCK = new int[128 * 128];
private static final int[] HAVE_BLOCK = new int[128 * 128];
private static boolean pause = false;
private static int settingCode = 1;
private static boolean hadFile = false;
private static boolean cancelOrStop = false;
private static boolean stop = false;
private static boolean arrive = false;
private static int haveBlockI = 0;
private static int maxNumber = 64;
@SubscribeEvent
public static void tick(TickEvent.ClientTickEvent event) {
if (mc.options.keyAttack.isDown() && mc.player != null) {
if (!attackDown) {
if (mc.player.getMainHandItem().getItem() == Items.FEATHER) {
if (doingJob) {
if (stop) {
stop = false;
cancelOrStop = false;
mc.player.sendMessage(new StringTextComponent("Resume!"), mc.player.getUUID());
}
} else {
if (mc.hitResult != null && mc.hitResult.getType().equals(RayTraceResult.Type.BLOCK)) {
if (set) {
DoJob();
set = false;
} else {
SetPoint();
}
} else {
if (!hadFile)
LoadingMap();
else {
hadFile = false;
mc.player.sendMessage(new StringTextComponent("The map file is no longer used!"), mc.player.getUUID());
}
}
}
}
if (mc.player.getMainHandItem().getItem() == Items.STICK) {
if (doingJob) {
if (!cancelOrStop) {
stop = true;
cancelOrStop = true;
assert mc.level != null;
mc.level.playSound(mc.player, mc.player.getX(), mc.player.getY(), mc.player.getZ(), SoundEvents.NOTE_BLOCK_PLING, SoundCategory.VOICE, 10, 10);
mc.player.sendMessage(new StringTextComponent("Pause! Click with feather to resume,or click with stick again to cancel."), mc.player.getUUID());
} else {
stop = false;
cancelOrStop = false;
EndJob();
mc.player.sendMessage(new StringTextComponent("Cancel!"), mc.player.getUUID());
}
} else {
if (mc.hitResult != null && mc.hitResult.getType().equals(RayTraceResult.Type.BLOCK))
CrashRestart();
else
Setting();
}
}
attackDown = true;
}
} else {
attackDown = false;
}
}
public static void GOGO() {
assert mc.player != null;
timer.schedule(new TimerTask() {
public void run() {
if (mc.level == null)
timer.cancel();
if (!stop) {
if (!hadFile)
ChangeBlock();
if (mc.player.getMainHandItem().getItem() == Items.AIR || mc.player.getMainHandItem().getItem() == Items.STICK || mc.player.getMainHandItem().getItem() == Items.FEATHER) {
if (!pause) {
mc.player.sendMessage(new StringTextComponent("Pause! Get some blocks in your hand to resume."), mc.player.getUUID());
pause = true;
}
} else {
pause = false;
if (!hadFile) {
if (doingJob)
SetAndRun();
} else {
mc.player.moveTo(base_x + 0.5 + x, base_y + 2, base_z + 0.5 + z);
WantGo();
if (arrive) {
ChooseAndSet();
arrive = false;
if (haveBlockI >= 128 * 128 - 1 || HAVE_BLOCK[haveBlockI] > maxNumber || HAVE_BLOCK[haveBlockI] == 0) {
EndJob();
mc.player.sendMessage(new StringTextComponent("Building completed according to the map!"), mc.player.getUUID());
}
}
}
}
}
}
}, 0, 100);
}
public static void DoJob() {
assert mc.player != null;
assert mc.gameMode != null;
doingJob = true;
timer = new Timer();
mc.player.sendMessage(new StringTextComponent("Start in 5s."), mc.player.getUUID());
timer.schedule(new TimerTask() {
public void run() {
GOGO();
assert mc.level != null;
mc.level.playSound(mc.player, mc.player.getX(), mc.player.getY(), mc.player.getZ(), SoundEvents.NOTE_BLOCK_PLING, SoundCategory.VOICE, 10, 10);
}
}, 5000);
}
public static void SetAndRun() {
assert mc.player != null;
SetBlock(base_x + x, base_y + 1, base_z + z);
mc.player.moveTo(base_x + 0.5 + x, base_y + 2, base_z + 0.5 + z);
if (positive && x < longX) {
x++;
}
if (!positive && x > -1) {
x--;
}
if (positive && x == longX) {
x--;
if (z < longZ - 1) {
z++;
} else {
EndJob();
mc.player.sendMessage(new StringTextComponent("Done!"), mc.player.getUUID());
}
positive = false;
}
if (!positive && x == -1) {
x++;
if (z < longZ - 1) {
z++;
} else {
EndJob();
mc.player.sendMessage(new StringTextComponent("Done!"), mc.player.getUUID());
}
positive = true;
}
}
public static void WantGo() {
assert mc.player != null;
number = HAVE_BLOCK[haveBlockI];
int want_z = number / longX;
int want_x = number % longX;
if (x == want_x && z == want_z) {
arrive = true;
haveBlockI++;
} else {
if (want_x < x)
if (x - want_x >= 4)
x -= 4;
else
x--;
else if (want_x > x)
if (want_x - x >= 4)
x += 4;
else
x++;
if (want_z > z)
if (want_z - z >= 4)
z += 4;
else
z++;
else if (want_z < z)
if (z - want_z >= 4)
z -= 4;
else
z--;
}
}
public static void ChooseAndSet() {
assert mc.player != null;
int BlockNumber = BLOCK[number] - 48;
if (BlockNumber > -1 && BlockNumber < 9)
mc.player.inventory.selected = BlockNumber;
SetBlock(base_x + x, base_y + 1, base_z + z);
}
public static void ChangeBlock() {
if (pause) {
assert mc.player != null;
if (mc.player.getMainHandItem().getItem() == Items.AIR)
if (mc.player.inventory.selected < 8) {
mc.player.inventory.selected++;
} else {
mc.player.inventory.selected = 0;
assert mc.level != null;
mc.level.playSound(mc.player, mc.player.getX(), mc.player.getY(), mc.player.getZ(), SoundEvents.NOTE_BLOCK_PLING, SoundCategory.VOICE, 10, 10);
mc.player.sendMessage(new StringTextComponent("No Block in bar!"), mc.player.getUUID());
}
}
}
public static void SetPoint() {
assert mc.hitResult != null;
if (first) {
set = false;
firstpoint = ((BlockRayTraceResult) mc.hitResult).getBlockPos();
assert mc.player != null;
mc.player.sendMessage(new StringTextComponent("Set the reference point to : " + firstpoint + " ?"), mc.player.getUUID());
mc.player.sendMessage(new StringTextComponent("Do it again to confirm the reference point."), mc.player.getUUID());
first = false;
} else if (((BlockRayTraceResult) mc.hitResult).getBlockPos().equals(firstpoint)) {
base_x = firstpoint.getX();
base_y = firstpoint.getY();
base_z = firstpoint.getZ();
assert mc.player != null;
mc.player.sendMessage(new StringTextComponent("Reference point : " + firstpoint + " ." + "Put your feather and stick in the first two bar,then hit any block with feather to start building."), mc.player.getUUID());
set = true;
} else {
first = true;
SetPoint();
}
}
public static void LoadingMap() {
assert mc.player != null;
boolean HadDir;
String path = ".\\CCMod";
File dir = new File(path);
File file = new File(path + "\\file.txt");
HadDir = !dir.mkdir();
if (!HadDir) {
mc.player.sendMessage(new StringTextComponent("A new folder named 'CCMod' has been created in the game folder."), mc.player.getUUID());
}
try {
hadFile = !file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
if (hadFile) {
mc.player.sendMessage(new StringTextComponent("The map file is loaded. Click with stick to set the target size."), mc.player.getUUID());
Reader reader;
try {
reader = new InputStreamReader(new FileInputStream(file));
int tempChar;
int i = 0;
int j = 0;
while ((tempChar = reader.read()) != -1) {
if (((char) tempChar) != '\n' && ((char) tempChar) != '\r') {
BLOCK[i] = (char) tempChar;
if (BLOCK[i] - 48 > -1 && BLOCK[i] - 48 < 9) {
HAVE_BLOCK[j] = i;
j++;
}
i++;
}
}
reader.close();
} catch (Exception e) {
e.printStackTrace();
}
} else {
mc.player.sendMessage(new StringTextComponent("A new folder named 'file.txt' has been created in 'CCMod'."), mc.player.getUUID());
}
}
public static void EndJob() {
assert mc.level != null;
assert mc.player != null;
timer.cancel();
x = 0;
z = 0;
number = 0;
doingJob = false;
positive = true;
pause = false;
cancelOrStop = false;
stop = false;
arrive = false;
haveBlockI = 0;
mc.level.playSound(mc.player, mc.player.getX(), mc.player.getY(), mc.player.getZ(), SoundEvents.NOTE_BLOCK_PLING, SoundCategory.VOICE, 10, 10);
}
public static void Setting() {
assert mc.player != null;
assert mc.level != null;
mc.level.playSound(mc.player, mc.player.getX(), mc.player.getY(), mc.player.getZ(), SoundEvents.NOTE_BLOCK_PLING, SoundCategory.VOICE, 10, 10);
if (settingCode > 16) {
settingCode = 1;
Setting();
} else {
longX = settingCode * 8;
longZ = settingCode * 8;
maxNumber = longX * longZ;
mc.player.sendMessage(new StringTextComponent("The size has been set to : " + settingCode * 8 + "*" + settingCode * 8), mc.player.getUUID());
settingCode++;
}
}
public static void SetBlock(int x, int y, int z) {
assert mc.player != null;
assert mc.level != null;
assert mc.gameMode != null;
Vector3d v3d = new Vector3d(x, y, z);
BlockPos blockpos = new BlockPos(v3d);
BlockRayTraceResult blockRTR = new BlockRayTraceResult(v3d, Direction.UP, blockpos, true);
mc.gameMode.useItemOn(mc.player, mc.level, Hand.MAIN_HAND, blockRTR);
}
public static void CrashRestart() {
assert mc.player != null;
if (!hadFile) {
mc.player.sendMessage(new StringTextComponent("This function only available in the map building mode."), mc.player.getUUID());
}
else if (set) {
assert mc.hitResult != null;
BlockPos restartPoint = ((BlockRayTraceResult) mc.hitResult).getBlockPos();
int restart_x = restartPoint.getX();
int restart_z = restartPoint.getZ();
mc.player.sendMessage(new StringTextComponent("Set the restart point(The last block built last time) to {X:" + restart_x + "," + "Z:" + restart_z + "}."), mc.player.getUUID());
mc.player.sendMessage(new StringTextComponent("After confirming that the other settings are correct, hit any block with feather to start building."), mc.player.getUUID());
x = restart_x - base_x;
z = restart_z - base_z;
int restartNumber = (restart_x - base_x) + (restart_z - base_z) * longX;
for (int i = 0; i < 128 * 128 - 1; i++) {
if (restartNumber == HAVE_BLOCK[i]) {
haveBlockI = i + 1;
}
}
} else
mc.player.sendMessage(new StringTextComponent("Set the reference point(Must be the same as last time) first!"), mc.player.getUUID());
}
}