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:
parent
480b865363
commit
e9b336192a
|
@ -7,6 +7,7 @@ use crate::shared::{Axis, Direction, Position};
|
||||||
use collision::Aabb3;
|
use collision::Aabb3;
|
||||||
use cgmath::Point3;
|
use cgmath::Point3;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub mod material;
|
pub mod material;
|
||||||
pub use self::material::Material;
|
pub use self::material::Material;
|
||||||
|
@ -44,12 +45,14 @@ macro_rules! create_ids {
|
||||||
struct VanillaIDMap {
|
struct VanillaIDMap {
|
||||||
flat: Vec<Option<Block>>,
|
flat: Vec<Option<Block>>,
|
||||||
hier: Vec<Option<Block>>,
|
hier: Vec<Option<Block>>,
|
||||||
|
modded: HashMap<String, [Option<Block>; 16]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! define_blocks {
|
macro_rules! define_blocks {
|
||||||
(
|
(
|
||||||
$(
|
$(
|
||||||
$name:ident {
|
$name:ident {
|
||||||
|
$(modid $modid:expr,)*
|
||||||
props {
|
props {
|
||||||
$(
|
$(
|
||||||
$fname:ident : $ftype:ty = [$($val:expr),+],
|
$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 {
|
if protocol_version >= 404 {
|
||||||
VANILLA_ID_MAP.flat.get(id).and_then(|v| *v).unwrap_or(Block::Missing{})
|
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 {
|
} 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 = {
|
static ref VANILLA_ID_MAP: VanillaIDMap = {
|
||||||
let mut blocks_flat = vec![];
|
let mut blocks_flat = vec![];
|
||||||
let mut blocks_hier = vec![];
|
let mut blocks_hier = vec![];
|
||||||
|
let mut blocks_modded: HashMap<String, [Option<Block>; 16]> = HashMap::new();
|
||||||
let mut flat_id = 0;
|
let mut flat_id = 0;
|
||||||
let mut last_internal_id = 0;
|
let mut last_internal_id = 0;
|
||||||
let mut hier_block_id = 0;
|
let mut hier_block_id = 0;
|
||||||
|
@ -353,6 +389,16 @@ macro_rules! define_blocks {
|
||||||
for block in iter {
|
for block in iter {
|
||||||
let internal_id = block.get_internal_id();
|
let internal_id = block.get_internal_id();
|
||||||
let hier_data: Option<usize> = block.get_hierarchical_data();
|
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 =
|
let vanilla_id =
|
||||||
if let Some(hier_data) = hier_data {
|
if let Some(hier_data) = hier_data {
|
||||||
if internal_id != last_internal_id {
|
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()),
|
data Some(color.data()),
|
||||||
model { ("minecraft", format!("{}_wool", color.as_string()) ) },
|
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 {
|
PistonExtension {
|
||||||
props {
|
props {
|
||||||
facing: Direction = [
|
facing: Direction = [
|
||||||
|
|
|
@ -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)]
|
#[derive(Debug)]
|
||||||
pub enum FmlHs {
|
pub enum FmlHs {
|
||||||
ServerHello {
|
ServerHello {
|
||||||
|
|
|
@ -711,17 +711,26 @@ impl Server {
|
||||||
|
|
||||||
self.write_fmlhs_plugin_message(&HandshakeAck { phase: WaitingServerData });
|
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");
|
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 });
|
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);
|
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 {
|
if !has_more {
|
||||||
self.write_fmlhs_plugin_message(&HandshakeAck { phase: WaitingServerComplete });
|
self.write_fmlhs_plugin_message(&HandshakeAck { phase: WaitingServerComplete });
|
||||||
}
|
}
|
||||||
// TODO: dynamically register mod blocks
|
|
||||||
},
|
},
|
||||||
HandshakeAck { phase } => {
|
HandshakeAck { phase } => {
|
||||||
match phase {
|
match phase {
|
||||||
|
@ -1285,7 +1294,7 @@ impl Server {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_block_change(&mut self, location: Position, id: i32) {
|
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) {
|
fn on_block_change_varint(&mut self, block_change: packet::play::clientbound::BlockChange_VarInt) {
|
||||||
|
@ -1309,7 +1318,7 @@ impl Server {
|
||||||
record.y as i32,
|
record.y as i32,
|
||||||
oz + (record.xz & 0xF) 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(
|
self.world.set_block(
|
||||||
Position::new(x, y, z),
|
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)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@ pub struct World {
|
||||||
block_entity_actions: VecDeque<BlockEntityAction>,
|
block_entity_actions: VecDeque<BlockEntityAction>,
|
||||||
|
|
||||||
protocol_version: i32,
|
protocol_version: i32,
|
||||||
|
pub modded_block_ids: HashMap<usize, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -619,7 +620,7 @@ impl World {
|
||||||
|
|
||||||
for bi in 0 .. 4096 {
|
for bi in 0 .. 4096 {
|
||||||
let id = data.read_u16::<byteorder::LittleEndian>()?;
|
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
|
// Spawn block entities
|
||||||
let b = section.blocks.get(bi);
|
let b = section.blocks.get(bi);
|
||||||
|
@ -805,7 +806,7 @@ impl World {
|
||||||
|
|
||||||
for bi in 0 .. 4096 {
|
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);
|
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
|
// Spawn block entities
|
||||||
let b = section.blocks.get(bi);
|
let b = section.blocks.get(bi);
|
||||||
|
@ -882,7 +883,7 @@ impl World {
|
||||||
let count = VarInt::read_from(&mut data)?.0;
|
let count = VarInt::read_from(&mut data)?.0;
|
||||||
for i in 0 .. count {
|
for i in 0 .. count {
|
||||||
let id = VarInt::read_from(&mut data)?.0;
|
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);
|
mappings.insert(i as usize, bl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -892,7 +893,7 @@ impl World {
|
||||||
|
|
||||||
for bi in 0 .. 4096 {
|
for bi in 0 .. 4096 {
|
||||||
let id = m.get(bi);
|
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
|
// Spawn block entities
|
||||||
let b = section.blocks.get(bi);
|
let b = section.blocks.get(bi);
|
||||||
if block_entity::BlockEntityType::get_block_entity(b).is_some() {
|
if block_entity::BlockEntityType::get_block_entity(b).is_some() {
|
||||||
|
|
Loading…
Reference in New Issue