From e9b336192a238f89f5da122982a6802d77379e32 Mon Sep 17 00:00:00 2001
From: iceiix <43691553+iceiix@users.noreply.github.com>
Date: Wed, 15 May 2019 12:22:24 -0700
Subject: [PATCH] Add support for a modded block: rockwool from Thermal
Expansion (#153)
The first in support for modded content, a simple custom block: "rockwool" from the Thermal Expansion and Thermal Foundation mods for Forge:
https://ftb.gamepedia.com/Rockwool_(Thermal_Expansion_3)
This makes use of the Forge handshake (#88 #134 #144), matching the mod block names from the negotiation to numeric identifiers in the world to steven_blocks. Rockwool was chosen due to ease of implementation, it looks like wool from vanilla (except is fire-proof), and by supporting it the groundwork necessary is laid for more sophisticated mod support.
Tested with Thermal Expansion on 1.7.10, 1.10.2 (FTB Beyond), and 1.12.2 Forge servers.
* Add `modid` macro token, skipped from vanilla mappings
* Add ThermalExpansionRockwool block (1.7.10)
* Register modded blocks by modid->[data], and lookup block metadata
* Save block IDs from ModIdData/RegistryData to World modded_block_ids
* Add namespaced mod ids for ModIdData, \u{1}=block \u{2}=item
* Add ThermalFoundation's Rockwool (1.12.2)
---
blocks/src/lib.rs | 102 ++++++++++++++++++++++++++++++++++++++++--
src/protocol/forge.rs | 3 ++
src/server/mod.rs | 23 +++++++---
src/world/mod.rs | 9 ++--
4 files changed, 123 insertions(+), 14 deletions(-)
diff --git a/blocks/src/lib.rs b/blocks/src/lib.rs
index bdbc9cc..1231b5c 100644
--- a/blocks/src/lib.rs
+++ b/blocks/src/lib.rs
@@ -7,6 +7,7 @@ use crate::shared::{Axis, Direction, Position};
use collision::Aabb3;
use cgmath::Point3;
use lazy_static::lazy_static;
+use std::collections::HashMap;
pub mod material;
pub use self::material::Material;
@@ -44,12 +45,14 @@ macro_rules! create_ids {
struct VanillaIDMap {
flat: Vec>,
hier: Vec >,
+ modded: HashMap; 16]>,
}
macro_rules! define_blocks {
(
$(
$name:ident {
+ $(modid $modid:expr,)*
props {
$(
$fname:ident : $ftype:ty = [$($val:expr),+],
@@ -133,11 +136,43 @@ macro_rules! define_blocks {
}
}
- pub fn by_vanilla_id(id: usize, protocol_version: i32) -> Block {
+ #[allow(unused_variables, unreachable_code)]
+ pub fn get_modid(&self) -> Option<&str> {
+ match *self {
+ $(
+ Block::$name {
+ $($fname,)*
+ } => {
+ $(
+ return Some($modid);
+ )*
+ None
+ }
+ )+
+ }
+ }
+
+ pub fn by_vanilla_id(id: usize, protocol_version: i32, modded_block_ids: &HashMap) -> Block {
if protocol_version >= 404 {
VANILLA_ID_MAP.flat.get(id).and_then(|v| *v).unwrap_or(Block::Missing{})
+ // TODO: support modded 1.13.2+ blocks after https://github.com/iceiix/stevenarella/pull/145
} else {
- VANILLA_ID_MAP.hier.get(id).and_then(|v| *v).unwrap_or(Block::Missing{})
+ if let Some(block) = VANILLA_ID_MAP.hier.get(id).and_then(|v| *v) {
+ block
+ } else {
+ let data = id & 0xf;
+
+ if let Some(name) = modded_block_ids.get(&(id >> 4)) {
+ if let Some(blocks_by_data) = VANILLA_ID_MAP.modded.get(name) {
+ blocks_by_data[data].unwrap_or(Block::Missing{})
+ } else {
+ //println!("Modded block not supported yet: {}:{} -> {}", id >> 4, data, name);
+ Block::Missing{}
+ }
+ } else {
+ Block::Missing{}
+ }
+ }
}
}
@@ -257,6 +292,7 @@ macro_rules! define_blocks {
static ref VANILLA_ID_MAP: VanillaIDMap = {
let mut blocks_flat = vec![];
let mut blocks_hier = vec![];
+ let mut blocks_modded: HashMap; 16]> = HashMap::new();
let mut flat_id = 0;
let mut last_internal_id = 0;
let mut hier_block_id = 0;
@@ -353,6 +389,16 @@ macro_rules! define_blocks {
for block in iter {
let internal_id = block.get_internal_id();
let hier_data: Option = block.get_hierarchical_data();
+ if let Some(modid) = block.get_modid() {
+ let hier_data = hier_data.unwrap();
+ if !blocks_modded.contains_key(modid) {
+ blocks_modded.insert(modid.to_string(), [None; 16]);
+ }
+ let block_from_data = blocks_modded.get_mut(modid).unwrap();
+ block_from_data[hier_data] = Some(block);
+ continue
+ }
+
let vanilla_id =
if let Some(hier_data) = hier_data {
if internal_id != last_internal_id {
@@ -422,7 +468,7 @@ macro_rules! define_blocks {
}
})+
- VanillaIDMap { flat: blocks_flat, hier: blocks_hier }
+ VanillaIDMap { flat: blocks_flat, hier: blocks_hier, modded: blocks_modded }
};
}
);
@@ -1014,6 +1060,56 @@ define_blocks! {
data Some(color.data()),
model { ("minecraft", format!("{}_wool", color.as_string()) ) },
}
+ ThermalExpansionRockwool {
+ modid "ThermalExpansion:Rockwool",
+ props {
+ color: ColoredVariant = [
+ ColoredVariant::White,
+ ColoredVariant::Orange,
+ ColoredVariant::Magenta,
+ ColoredVariant::LightBlue,
+ ColoredVariant::Yellow,
+ ColoredVariant::Lime,
+ ColoredVariant::Pink,
+ ColoredVariant::Gray,
+ ColoredVariant::Silver,
+ ColoredVariant::Cyan,
+ ColoredVariant::Purple,
+ ColoredVariant::Blue,
+ ColoredVariant::Brown,
+ ColoredVariant::Green,
+ ColoredVariant::Red,
+ ColoredVariant::Black
+ ],
+ },
+ data Some(color.data()),
+ model { ("minecraft", format!("{}_wool", color.as_string()) ) },
+ }
+ ThermalFoundationRockwool {
+ modid "thermalfoundation:rockwool",
+ props {
+ color: ColoredVariant = [
+ ColoredVariant::White,
+ ColoredVariant::Orange,
+ ColoredVariant::Magenta,
+ ColoredVariant::LightBlue,
+ ColoredVariant::Yellow,
+ ColoredVariant::Lime,
+ ColoredVariant::Pink,
+ ColoredVariant::Gray,
+ ColoredVariant::Silver,
+ ColoredVariant::Cyan,
+ ColoredVariant::Purple,
+ ColoredVariant::Blue,
+ ColoredVariant::Brown,
+ ColoredVariant::Green,
+ ColoredVariant::Red,
+ ColoredVariant::Black
+ ],
+ },
+ data Some(color.data()),
+ model { ("minecraft", format!("{}_wool", color.as_string()) ) },
+ }
PistonExtension {
props {
facing: Direction = [
diff --git a/src/protocol/forge.rs b/src/protocol/forge.rs
index 841df92..005a887 100644
--- a/src/protocol/forge.rs
+++ b/src/protocol/forge.rs
@@ -85,6 +85,9 @@ impl Serializable for ModIdMapping {
}
}
+pub static BLOCK_NAMESPACE: &'static str = "\u{1}";
+pub static ITEM_NAMESPACE: &'static str = "\u{2}";
+
#[derive(Debug)]
pub enum FmlHs {
ServerHello {
diff --git a/src/server/mod.rs b/src/server/mod.rs
index 9be139a..dcc623a 100644
--- a/src/server/mod.rs
+++ b/src/server/mod.rs
@@ -711,17 +711,26 @@ impl Server {
self.write_fmlhs_plugin_message(&HandshakeAck { phase: WaitingServerData });
},
- ModIdData { mappings: _, block_substitutions: _, item_substitutions: _ } => {
+ ModIdData { mappings, block_substitutions: _, item_substitutions: _ } => {
println!("Received FML|HS ModIdData");
+ for m in mappings.data {
+ let (namespace, name) = m.name.split_at(1);
+ if namespace == protocol::forge::BLOCK_NAMESPACE {
+ self.world.modded_block_ids.insert(m.id.0 as usize, name.to_string());
+ }
+ }
self.write_fmlhs_plugin_message(&HandshakeAck { phase: WaitingServerComplete });
- // TODO: dynamically register mod blocks
},
- RegistryData { has_more, name, ids: _, substitutions: _, dummies: _ } => {
+ RegistryData { has_more, name, ids, substitutions: _, dummies: _ } => {
println!("Received FML|HS RegistryData for {}", name);
+ if name == "minecraft:blocks" {
+ for m in ids.data {
+ self.world.modded_block_ids.insert(m.id.0 as usize, m.name);
+ }
+ }
if !has_more {
self.write_fmlhs_plugin_message(&HandshakeAck { phase: WaitingServerComplete });
}
- // TODO: dynamically register mod blocks
},
HandshakeAck { phase } => {
match phase {
@@ -1285,7 +1294,7 @@ impl Server {
}
fn on_block_change(&mut self, location: Position, id: i32) {
- self.world.set_block(location, block::Block::by_vanilla_id(id as usize, self.protocol_version))
+ self.world.set_block(location, block::Block::by_vanilla_id(id as usize, self.protocol_version, &self.world.modded_block_ids))
}
fn on_block_change_varint(&mut self, block_change: packet::play::clientbound::BlockChange_VarInt) {
@@ -1309,7 +1318,7 @@ impl Server {
record.y as i32,
oz + (record.xz & 0xF) as i32
),
- block::Block::by_vanilla_id(record.block_id.0 as usize, self.protocol_version)
+ block::Block::by_vanilla_id(record.block_id.0 as usize, self.protocol_version, &self.world.modded_block_ids)
);
}
}
@@ -1332,7 +1341,7 @@ impl Server {
self.world.set_block(
Position::new(x, y, z),
- block::Block::by_vanilla_id(id as usize, self.protocol_version)
+ block::Block::by_vanilla_id(id as usize, self.protocol_version, &self.world.modded_block_ids)
);
}
}
diff --git a/src/world/mod.rs b/src/world/mod.rs
index 87f4c28..6087d16 100644
--- a/src/world/mod.rs
+++ b/src/world/mod.rs
@@ -45,6 +45,7 @@ pub struct World {
block_entity_actions: VecDeque,
protocol_version: i32,
+ pub modded_block_ids: HashMap,
}
#[derive(Clone, Debug)]
@@ -619,7 +620,7 @@ impl World {
for bi in 0 .. 4096 {
let id = data.read_u16::()?;
- section.blocks.set(bi, block::Block::by_vanilla_id(id as usize, self.protocol_version));
+ section.blocks.set(bi, block::Block::by_vanilla_id(id as usize, self.protocol_version, &self.modded_block_ids));
// Spawn block entities
let b = section.blocks.get(bi);
@@ -805,7 +806,7 @@ impl World {
for bi in 0 .. 4096 {
let id = ((block_add[i].get(bi) as u16) << 12) | ((block_types[i][bi] as u16) << 4) | (block_meta[i].get(bi) as u16);
- section.blocks.set(bi, block::Block::by_vanilla_id(id as usize, self.protocol_version));
+ section.blocks.set(bi, block::Block::by_vanilla_id(id as usize, self.protocol_version, &self.modded_block_ids));
// Spawn block entities
let b = section.blocks.get(bi);
@@ -882,7 +883,7 @@ impl World {
let count = VarInt::read_from(&mut data)?.0;
for i in 0 .. count {
let id = VarInt::read_from(&mut data)?.0;
- let bl = block::Block::by_vanilla_id(id as usize, self.protocol_version);
+ let bl = block::Block::by_vanilla_id(id as usize, self.protocol_version, &self.modded_block_ids);
mappings.insert(i as usize, bl);
}
}
@@ -892,7 +893,7 @@ impl World {
for bi in 0 .. 4096 {
let id = m.get(bi);
- section.blocks.set(bi, mappings.get(&id).cloned().unwrap_or(block::Block::by_vanilla_id(id, self.protocol_version)));
+ section.blocks.set(bi, mappings.get(&id).cloned().unwrap_or(block::Block::by_vanilla_id(id, self.protocol_version, &self.modded_block_ids)));
// Spawn block entities
let b = section.blocks.get(bi);
if block_entity::BlockEntityType::get_block_entity(b).is_some() {