Игрок: хитбокс 1.45 в шифте и прыжок с места 1.5
- RsxPlayerTuning: высота при подкрадывании 1.45 (ванилла 1.65), прыжок с места 1.5, в движении ванильные 1.25, высота глаз в шифте 1.37. - RsxPlayer: прыжок по наличию горизонтального ввода, глаза при подкрадывании. - Патч в EntityPlayer.updateSize: высота при подкрадывании из нашего класса. - Импульс прыжка считается обратным счетом по физике ваниллы. AABB: редактор хитбоксов и их отрисовка - Протокол: Hitbox (имя, зона, min/max, поворот), пакеты Hitboxes, HitboxInstances и HitboxesReload. - Сервер: HitboxRegistry читает JSON-файл (world/hitboxes.json, можно указать --hitboxes), перечитывает его по запросу клиента и шлет наборы игрокам. Сломанный файл не стирает рабочие коробки, а объясняет причину. - Клиент: RsxHitboxDebug рисует коробки через RenderGlobal.drawBoundingBox с подписями DebugRenderer, красит зоны (голова красная, тело желтое, ноги синие). Команды: /rsx hitboxes on|off|reload|list|show NAME. - Новый модуль rsx-plugin-api с интерфейсом Hitboxes: плагин может зарегистрировать свои коробки. У сервера появился скрипт сборки. Тесты: протокол 34, сервер 80 (было 74). Форк собирается чисто, 3279 классов.
This commit is contained in:
@@ -0,0 +1,263 @@
|
||||
package net.rustnotmc.client;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.entity.EntityPlayerSP;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.RenderGlobal;
|
||||
import net.minecraft.client.renderer.debug.DebugRenderer;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.rustnotmc.protocol.Hitbox;
|
||||
import net.rustnotmc.protocol.Packets;
|
||||
|
||||
/**
|
||||
* Draws the boxes of the models.
|
||||
*
|
||||
* The boxes are the editor: the server reads them from a file and sends them
|
||||
* here, and they are drawn around the things they belong to. A model that
|
||||
* does not fit is changed in the file, the file is read again, and the new
|
||||
* numbers are on the screen.
|
||||
*
|
||||
* The command is:
|
||||
*
|
||||
* <pre>
|
||||
* /rsx hitboxes on show the boxes
|
||||
* /rsx hitboxes off hide them
|
||||
* /rsx hitboxes reload ask the server to read its file again
|
||||
* /rsx hitboxes list what the server knows
|
||||
* /rsx hitboxes show NAME put one model in front of the player
|
||||
* </pre>
|
||||
*/
|
||||
public final class RsxHitboxDebug {
|
||||
|
||||
/** The model whose boxes describe a player. */
|
||||
private static final String PLAYER_MODEL = "player";
|
||||
|
||||
/** The height the player model is written for. */
|
||||
private static final float PLAYER_MODEL_HEIGHT = 1.8F;
|
||||
|
||||
private static final Map<String, Packets.HitboxModel> MODELS =
|
||||
new LinkedHashMap<String, Packets.HitboxModel>();
|
||||
private static final List<Packets.HitboxInstance> INSTANCES =
|
||||
new ArrayList<Packets.HitboxInstance>();
|
||||
|
||||
private static boolean visible;
|
||||
private static String preview;
|
||||
|
||||
private RsxHitboxDebug() {
|
||||
}
|
||||
|
||||
/** Takes the boxes of the models from the server. */
|
||||
public static void accept(Packets.Hitboxes boxes) {
|
||||
MODELS.clear();
|
||||
for (int i = 0; i < boxes.models.length; i++) {
|
||||
MODELS.put(boxes.models[i].name, boxes.models[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/** Takes the places where the models stand. */
|
||||
public static void accept(Packets.HitboxInstances instances) {
|
||||
INSTANCES.clear();
|
||||
for (int i = 0; i < instances.instances.length; i++) {
|
||||
INSTANCES.add(instances.instances[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/** True when the boxes are being drawn. */
|
||||
public static boolean visible() {
|
||||
return visible;
|
||||
}
|
||||
|
||||
/** How many models the server has sent. */
|
||||
public static int modelCount() {
|
||||
return MODELS.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Carries out one of our commands.
|
||||
*
|
||||
* Returns true when the line was ours, so that the mods do not see it.
|
||||
*/
|
||||
public static boolean command(String line) {
|
||||
if (line == null || !line.startsWith("/")) {
|
||||
return false;
|
||||
}
|
||||
String[] parts = line.substring(1).trim().split("\\s+");
|
||||
if (parts.length < 2 || !"rsx".equalsIgnoreCase(parts[0])
|
||||
|| !"hitboxes".equalsIgnoreCase(parts[1])) {
|
||||
return false;
|
||||
}
|
||||
String what = parts.length > 2 ? parts[2].toLowerCase() : "";
|
||||
|
||||
if ("on".equals(what)) {
|
||||
visible = true;
|
||||
say("hitboxes on (" + MODELS.size() + " models)");
|
||||
return true;
|
||||
}
|
||||
if ("off".equals(what)) {
|
||||
visible = false;
|
||||
say("hitboxes off");
|
||||
return true;
|
||||
}
|
||||
if ("reload".equals(what)) {
|
||||
net.rustnotmc.client.net.RsxNetworkManager.sendHitboxesReload();
|
||||
say("the server was asked to read its file again");
|
||||
return true;
|
||||
}
|
||||
if ("list".equals(what)) {
|
||||
StringBuilder names = new StringBuilder();
|
||||
for (String name : MODELS.keySet()) {
|
||||
if (names.length() > 0) {
|
||||
names.append(", ");
|
||||
}
|
||||
names.append(name);
|
||||
}
|
||||
say("models: " + (names.length() == 0 ? "none" : names.toString()));
|
||||
return true;
|
||||
}
|
||||
if ("show".equals(what)) {
|
||||
if (parts.length < 4) {
|
||||
say("usage: /rsx hitboxes show NAME");
|
||||
return true;
|
||||
}
|
||||
preview = parts[3];
|
||||
visible = true;
|
||||
say("showing " + preview);
|
||||
return true;
|
||||
}
|
||||
say("usage: /rsx hitboxes on|off|reload|list|show NAME");
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void say(String message) {
|
||||
net.rustnotmc.loader.Log.info("hitboxes: " + message);
|
||||
Minecraft mc = Minecraft.getMinecraft();
|
||||
if (mc.ingameGUI != null) {
|
||||
mc.ingameGUI.addChatMessage(net.minecraft.util.text.ChatType.CHAT,
|
||||
new net.minecraft.util.text.TextComponentString(message));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the boxes.
|
||||
*
|
||||
* Called from the frame hook, so this is after the world and after the
|
||||
* interface: the lines are drawn over both, which is what a debug view
|
||||
* should do.
|
||||
*/
|
||||
public static void draw(Minecraft mc) {
|
||||
if (!visible || mc.player == null || mc.world == null) {
|
||||
return;
|
||||
}
|
||||
Entity view = mc.getRenderViewEntity();
|
||||
if (view == null) {
|
||||
return;
|
||||
}
|
||||
float partial = mc.getRenderPartialTicks();
|
||||
double camX = view.lastTickPosX + (view.posX - view.lastTickPosX) * partial;
|
||||
double camY = view.lastTickPosY + (view.posY - view.lastTickPosY) * partial;
|
||||
double camZ = view.lastTickPosZ + (view.posZ - view.lastTickPosZ) * partial;
|
||||
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.tryBlendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA,
|
||||
GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA,
|
||||
GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO);
|
||||
GlStateManager.glLineWidth(2.0F);
|
||||
GlStateManager.disableTexture2D();
|
||||
GlStateManager.depthMask(false);
|
||||
|
||||
for (int i = 0; i < INSTANCES.size(); i++) {
|
||||
Packets.HitboxInstance instance = INSTANCES.get(i);
|
||||
Packets.HitboxModel model = MODELS.get(instance.model);
|
||||
if (model == null) {
|
||||
continue;
|
||||
}
|
||||
drawModel(model, instance.x, instance.y, instance.z, instance.yaw,
|
||||
camX, camY, camZ, 1.0F);
|
||||
}
|
||||
|
||||
if (preview != null) {
|
||||
Packets.HitboxModel model = MODELS.get(preview);
|
||||
if (model != null) {
|
||||
EntityPlayerSP player = mc.player;
|
||||
drawModel(model, player.posX + 1.0D, player.posY, player.posZ,
|
||||
player.rotationYaw, camX, camY, camZ, 1.0F);
|
||||
}
|
||||
}
|
||||
|
||||
// and the players, with the zones the damage is counted by
|
||||
Packets.HitboxModel player = MODELS.get(PLAYER_MODEL);
|
||||
if (player != null) {
|
||||
List<Entity> players = mc.world.getLoadedEntityList();
|
||||
for (int i = 0; i < players.size(); i++) {
|
||||
Entity entity = players.get(i);
|
||||
if (!(entity instanceof EntityPlayerSP)) {
|
||||
continue;
|
||||
}
|
||||
float scale = entity.height / PLAYER_MODEL_HEIGHT;
|
||||
drawModel(player, entity.posX, entity.posY, entity.posZ,
|
||||
entity.rotationYaw, camX, camY, camZ, scale);
|
||||
}
|
||||
}
|
||||
|
||||
GlStateManager.depthMask(true);
|
||||
GlStateManager.enableTexture2D();
|
||||
GlStateManager.disableBlend();
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws one model.
|
||||
*
|
||||
* The boxes of the model are turned by the yaw of the thing and moved to
|
||||
* where it stands, and then drawn relative to the camera, because that is
|
||||
* how the world is drawn too.
|
||||
*/
|
||||
private static void drawModel(Packets.HitboxModel model, double x, double y, double z,
|
||||
float yaw, double camX, double camY, double camZ, float scale) {
|
||||
double cos = Math.cos(Math.toRadians(yaw));
|
||||
double sin = Math.sin(Math.toRadians(yaw));
|
||||
for (int i = 0; i < model.boxes.length; i++) {
|
||||
Hitbox box = model.boxes[i];
|
||||
float[] colour = colourOf(box.part);
|
||||
double minX = turn(box.minX * scale, box.minZ * scale, cos, sin);
|
||||
double minZ = turn(box.minZ * scale, -box.minX * scale, cos, sin);
|
||||
double maxX = turn(box.maxX * scale, box.maxZ * scale, cos, sin);
|
||||
double maxZ = turn(box.maxZ * scale, -box.maxX * scale, cos, sin);
|
||||
double minY = box.minY * scale;
|
||||
double maxY = box.maxY * scale;
|
||||
|
||||
RenderGlobal.drawBoundingBox(
|
||||
x + minX - camX, y + minY - camY, z + minZ - camZ,
|
||||
x + maxX - camX, y + maxY - camY, z + maxZ - camZ,
|
||||
colour[0], colour[1], colour[2], 0.9F);
|
||||
|
||||
if (!box.name.isEmpty()) {
|
||||
DebugRenderer.renderDebugText(box.name,
|
||||
x + minX, y + maxY, z + minZ, 0.02F, 0xFFFFFFFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Turns a point around the vertical axis. */
|
||||
private static double turn(double along, double across, double cos, double sin) {
|
||||
return along * cos + across * sin;
|
||||
}
|
||||
|
||||
/** The colour of a box: the head is red, the body yellow, the legs blue. */
|
||||
private static float[] colourOf(String part) {
|
||||
if (Hitbox.PART_HEAD.equals(part)) {
|
||||
return new float[]{1.0F, 0.25F, 0.25F};
|
||||
}
|
||||
if (Hitbox.PART_BODY.equals(part)) {
|
||||
return new float[]{1.0F, 0.9F, 0.2F};
|
||||
}
|
||||
if (Hitbox.PART_LEGS.equals(part)) {
|
||||
return new float[]{0.3F, 0.55F, 1.0F};
|
||||
}
|
||||
return new float[]{0.3F, 1.0F, 0.4F};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user