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)
This commit is contained in:
iceiix 2019-05-15 12:22:24 -07:00 committed by ice_iix
parent 480b865363
commit e9b336192a
4 changed files with 123 additions and 14 deletions

View File

@ -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<Option<Block>>,
hier: Vec<Option<Block>>,
modded: HashMap<String, [Option<Block>; 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<usize, String>) -> 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<String, [Option<Block>; 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<usize> = 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 = [

View File

@ -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 {

View File

@ -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)
);
}
}

View File

@ -45,6 +45,7 @@ pub struct World {
block_entity_actions: VecDeque<BlockEntityAction>,
protocol_version: i32,
pub modded_block_ids: HashMap<usize, String>,
}
#[derive(Clone, Debug)]
@ -619,7 +620,7 @@ impl World {
for bi in 0 .. 4096 {
let id = data.read_u16::<byteorder::LittleEndian>()?;
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() {