Restructured directories

This commit is contained in:
Chicken-Bones committed 2014-04-10 17:32:38 +10:00
1 parent b5686f8df8
commit ec73305f0e
105 files changed
-11824

No files matched your search

-33
View File
@@ -1,33 +0,0 @@
#ant stuff
/bin/
#Remove OS generated garbage
*/.DS_Store
.DS_Store
.DS_Store?
.Spotlight-V100
.Trashes
Icon?
ehthumbs.db
Thumbs.db
#gradle stuff
/.gradle
/build/
/run/
#IDEA files from Gradle
.idea/
/*.iml
/*.ipr
/*.iws
#Vim backups
*~
#eclipse stuffs
/.classpath
/.project
/.settings/
/debug/
*.lock
/.metadata/
/config/
/logs/
options.txt
/saves/
-164
View File
@@ -1,164 +0,0 @@
# Blender v2.64 (sub 0) OBJ File: 'hackSaw.blend'
# www.blender.org
g Handle
v 0.039250 0.068563 -0.108650
v 0.039250 -0.196237 -0.108650
v -0.039250 -0.196237 -0.108650
v -0.039250 0.068563 -0.108650
v 0.039250 0.196263 0.108650
v -0.039250 0.196263 0.108650
v -0.039250 -0.068537 0.108650
v 0.039250 -0.068537 0.108650
v -0.039250 -0.126338 -0.068650
v -0.039250 0.045662 -0.068650
v -0.039250 -0.045638 0.068650
v -0.039250 0.126263 0.068650
v 0.039250 0.045662 -0.068650
v 0.039250 -0.126338 -0.068650
v 0.039250 0.126263 0.068650
v 0.039250 -0.045638 0.068650
vt 0.250000 0.562500
vt 0.250000 0.187500
vt 0.312500 0.250000
vt 0.312500 0.500000
vt 0.625000 0.187500
vt 0.562500 0.250000
vt 0.625000 0.562500
vt 0.562500 0.500000
vt 0.625000 0.000000
vt 1.000000 0.000000
vt 1.000000 0.093750
vt 0.625000 0.093750
vt 1.000000 0.187500
vt 0.250000 0.093750
vt 0.250000 0.000000
vt 0.875000 0.468750
vt 0.625000 0.468750
vt 0.625000 0.375000
vt 0.875000 0.375000
vt 0.875000 0.562500
vt 0.875000 0.187500
vt 0.875000 0.281250
vt 0.625000 0.281250
vn -1.000000 0.000000 -0.000000
vn 1.000000 0.000000 0.000000
vn 0.610523 0.393323 -0.687368
vn 0.554430 -0.722312 -0.413312
vn -0.554430 -0.722312 -0.413312
vn -0.610523 0.393323 -0.687368
vn 0.554430 0.722312 0.413312
vn -0.554430 0.722312 0.413312
vn -0.610523 -0.393323 0.687368
vn 0.610523 -0.393323 0.687368
vn -0.958464 0.247505 0.141606
vn -0.837581 0.271279 -0.474136
vn 0.837581 0.271279 -0.474136
vn 0.958464 0.247505 0.141606
vn -0.958403 -0.247597 -0.141758
vn 0.958403 -0.247597 -0.141758
vn -0.837672 -0.271340 0.473952
vn 0.837672 -0.271340 0.473952
s off
f 4/1/1 3/2/1 9/3/1 10/4/1
f 3/2/1 7/5/1 11/6/1 9/3/1
f 7/5/1 6/7/1 12/8/1 11/6/1
f 6/7/1 4/1/1 10/4/1 12/8/1
f 2/2/2 1/1/2 13/4/2 14/3/2
f 1/1/2 5/7/2 15/8/2 13/4/2
f 5/7/2 8/5/2 16/6/2 15/8/2
f 8/5/2 2/2/2 14/3/2 16/6/2
s 1
f 1/9/3 2/10/4 3/11/5 4/12/6
f 5/13/7 6/11/8 7/12/9 8/5/10
f 1/9/3 4/12/6 6/14/8 5/15/7
f 3/14/5 2/2/4 8/5/10 7/12/9
f 9/16/11 11/17/12 16/18/13 14/19/14
f 11/20/12 12/7/15 15/17/16 16/16/13
f 12/5/15 10/21/17 13/22/18 15/23/16
f 10/23/17 9/22/11 14/19/14 13/18/18
g Blade
v 0.000000 0.067100 -0.081925
v 0.000000 0.732900 -0.081925
v 0.000000 0.732900 -0.107925
v 0.000000 0.067100 -0.107925
vt 1.000000 1.000000
vt 0.187500 1.000000
vt 0.187500 0.953125
vt 1.000000 0.953125
vn 0.999969 0.000000 0.000000
s 1
f 18/24/19 17/25/2 20/26/19 19/27/2
g BladeSupport
v -0.025100 0.713937 -0.081525
v 0.025100 0.719137 0.036975
v 0.025100 0.167037 0.036975
v 0.025100 0.713937 -0.081525
v 0.025100 0.739237 -0.022325
v -0.025100 0.739237 -0.022325
v -0.025100 0.167037 0.036975
v -0.025100 0.719137 0.036975
v 0.025100 0.748037 -0.127225
v 0.025100 0.794437 0.003175
v -0.025100 0.794437 0.003175
v -0.025100 0.748037 -0.127225
v 0.025100 0.144237 0.076975
v -0.025100 0.144237 0.076975
v -0.025100 0.773937 0.076975
v 0.025100 0.773937 0.076975
vt 0.062500 0.859375
vt 0.062500 0.750000
vt 0.125000 0.750000
vt 0.125000 0.859375
vt 0.125000 1.000000
vt 0.062500 1.000000
vt 0.187500 0.140625
vt 0.125000 0.140625
vt 0.125000 0.062500
vt 0.187500 0.062500
vt 0.125000 0.937500
vt 0.187500 0.937500
vt 0.187500 1.000000
vt 0.062500 0.000000
vt 0.125000 0.000000
vt 0.187500 0.203125
vt 0.125000 0.203125
vt 0.000000 0.750000
vt 0.000000 0.859375
vt 0.187500 0.281250
vt 0.125000 0.281250
vt 0.000000 1.000000
vt 0.000000 0.000000
vn 0.000000 -0.801471 -0.598034
vn 0.000000 -0.868777 -0.495203
vn -0.610157 -0.786706 -0.093722
vn 0.610157 -0.786706 -0.093722
vn 0.796991 -0.603504 0.023255
vn -0.796991 -0.603504 0.023255
vn -0.592944 0.487259 0.641072
vn 0.592944 0.487259 0.641072
vn 0.645070 0.763604 -0.027070
vn -0.645070 0.763604 -0.027070
vn 0.864193 -0.293130 -0.408887
vn -0.864193 -0.293130 -0.408887
vn -0.609577 -0.398236 -0.685385
vn 0.609577 -0.398236 -0.685385
vn -0.552568 0.124210 -0.824152
vn 0.552568 0.124210 -0.824152
vn -0.554888 -0.719291 0.417951
vn 0.554888 -0.719291 0.417951
s off
f 30/28/2 36/29/2 22/30/2 25/31/2
f 21/32/1 26/31/1 31/28/1 32/33/1
f 24/34/20 21/35/20 32/36/20 29/37/20
f 27/38/21 23/39/21 33/40/21 34/32/21
f 36/29/2 33/41/2 23/42/2 22/30/2
f 30/28/2 25/31/2 24/32/2 29/33/2
f 31/28/1 26/31/1 28/30/1 35/29/1
f 34/41/1 35/29/1 28/30/1 27/42/1
s 1
f 21/35/22 24/34/23 25/43/24 26/44/25
f 35/45/26 36/29/27 30/28/28 31/46/29
f 26/44/25 25/43/24 22/47/30 28/48/31
f 27/38/32 28/48/31 22/47/30 23/39/33
f 32/49/34 31/46/29 30/28/28 29/33/35
f 36/29/27 35/45/26 34/50/36 33/41/37
Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 531 B

-32
View File
@@ -1,32 +0,0 @@
item.microblock:sawStone.name=Steinsäge
item.microblock:sawIron.name=Eisensäge
item.microblock:sawDiamond.name=Diamantsäge
item.microblock:stoneRod.name=Steinstab
mcr_face.1.name=%s-Cover
mcr_face.1.subset=Cover
mcr_face.2.name=%s-Platte
mcr_face.2.subset=Platte
mcr_face.4.name=%s-Scheibe
mcr_face.4.subset=Scheibe
mcr_hllw.1.name=Hohles %s-Cover
mcr_hllw.1.subset=Hohles Cover
mcr_hllw.2.name=Hohles %s-Platte
mcr_hllw.2.subset=Hohle Platte
mcr_hllw.4.name=Hohle %s-Scheibe
mcr_hllw.4.subset=Hohle Scheibe
mcr_edge.1.name=%s-Streifen
mcr_edge.1.subset=Streifen
mcr_edge.2.name=%s-Pfosten
mcr_edge.2.subset=Pfosten
mcr_edge.4.name=%s-Pfeiler
mcr_edge.4.subset=Pfeiler
mcr_cnr.1.name=%s-Stückchen
mcr_cnr.1.subset=Stückchen
mcr_cnr.2.name=%s-Ecke
mcr_cnr.2.subset=Ecke
mcr_cnr.4.name=%s-Kerbe
mcr_cnr.4.subset=Kerbe
-35
View File
@@ -1,35 +0,0 @@
microblock.missing=The following microblocks are not installed on this client: %s
multipart.missing=The following multiparts are not installed on this client: %s
item.microblock:sawStone.name=Stone Saw
item.microblock:sawIron.name=Iron Saw
item.microblock:sawDiamond.name=Diamond Saw
item.microblock:stoneRod.name=Stone Rod
mcr_face.1.name=%s Cover
mcr_face.1.subset=Cover
mcr_face.2.name=%s Panel
mcr_face.2.subset=Panel
mcr_face.4.name=%s Slab
mcr_face.4.subset=Slab
mcr_hllw.1.name=Hollow %s Cover
mcr_hllw.1.subset=Hollow Cover
mcr_hllw.2.name=Hollow %s Panel
mcr_hllw.2.subset=Hollow Panel
mcr_hllw.4.name=Hollow %s Slab
mcr_hllw.4.subset=Hollow Slab
mcr_edge.1.name=%s Strip
mcr_edge.1.subset=Strip
mcr_edge.2.name=%s Post
mcr_edge.2.subset=Post
mcr_edge.4.name=%s Pillar
mcr_edge.4.subset=Pillar
mcr_cnr.1.name=%s Nook
mcr_cnr.1.subset=Nook
mcr_cnr.2.name=%s Corner
mcr_cnr.2.subset=Corner
mcr_cnr.4.name=%s Notch
mcr_cnr.4.subset=Notch
-32
View File
@@ -1,32 +0,0 @@
item.microblock:sawStone.name=Sierra de Piedra
item.microblock:sawIron.name=Sierra de Hierro
item.microblock:sawDiamond.name=Sierra de Diamante
item.microblock:stoneRod.name=Palo de Piedra
mcr_face.1.name=Cubierta de %s
mcr_face.1.subset=Cubierta
mcr_face.2.name=Panel de %s
mcr_face.2.subset=Panel
mcr_face.4.name=Losa de %s
mcr_face.4.subset=Losa
mcr_hllw.1.name=Cubierta Hueca de %s
mcr_hllw.1.subset=Cubierta Hueca
mcr_hllw.2.name=Panel Hueco de %s
mcr_hllw.2.subset=Panel Hueco
mcr_hllw.4.name=Losa Hueca de %s
mcr_hllw.4.subset=Losa Hueca
mcr_edge.1.name=Listón de %s
mcr_edge.1.subset=Listón
mcr_edge.2.name=Poste de %s
mcr_edge.2.subset=Poste
mcr_edge.4.name=Pilar de %s
mcr_edge.4.subset=Pilar
mcr_cnr.1.name=Punta de %s
mcr_cnr.1.subset=Punta
mcr_cnr.2.name=Esquina de %s
mcr_cnr.2.subset=Esquina
mcr_cnr.4.name=Cuarto de %s
mcr_cnr.4.subset=Cuarto
-32
View File
@@ -1,32 +0,0 @@
item.microblock:sawStone.name=Scie en pierre
item.microblock:sawIron.name=Scie en fer
item.microblock:sawDiamond.name=Scie en diamant
item.microblock:stoneRod.name=Bâtons en pierre
mcr_face.1.name=Couvercle de %s
mcr_face.1.subset=Couvercle
mcr_face.2.name=Panneau de %s
mcr_face.2.subset=Panneau
mcr_face.4.name=Dalle de %s
mcr_face.4.subset=Dalle
mcr_hllw.1.name=Couvercle creux de %s
mcr_hllw.1.subset=Couvercle creux
mcr_hllw.2.name=Panneau creux de %s
mcr_hllw.2.subset=Panneau creux
mcr_hllw.4.name=Dalle creuse de %s
mcr_hllw.4.subset=Dalle creuse
mcr_edge.1.name=Bande de %s
mcr_edge.1.subset=Bande
mcr_edge.2.name=Pieu de %s
mcr_edge.2.subset=Pieu
mcr_edge.4.name=Pilier de %s
mcr_edge.4.subset=Pilier
mcr_cnr.1.name=Coin de %s
mcr_cnr.1.subset=Coin
mcr_cnr.2.name=Bordure de %s
mcr_cnr.2.subset=Bordure
mcr_cnr.4.name=Entaille de %s
mcr_cnr.4.subset=Entaille
-32
View File
@@ -1,32 +0,0 @@
item.microblock:sawStone.name=Kamena Pila
item.microblock:sawIron.name=Željezna Pila
item.microblock:sawDiamond.name=Dijamantna Pila
item.microblock:stoneRod.name=Kameni Štap
mcr_face.1.name=%si Pokrivač
mcr_face.1.subset=Pokrivač
mcr_face.2.name=%si Panel
mcr_face.2.subset=Panel
mcr_face.4.name=%sa Ploča
mcr_face.4.subset=Ploča
mcr_hllw.1.name=Izdubljen %si Pokrivač
mcr_hllw.1.subset=Izdubljen Pokrivač
mcr_hllw.2.name=Izdubljen %si Panel
mcr_hllw.2.subset=Izdubljen Panel
mcr_hllw.4.name=Izdubljena %sa Ploča
mcr_hllw.4.subset=Izdubljena Ploča
mcr_edge.1.name=%sa Pruga
mcr_edge.1.subset=Pruga
mcr_edge.2.name=%si Post
mcr_edge.2.subset=Post
mcr_edge.4.name=%si Stup
mcr_edge.4.subset=Stup
mcr_cnr.1.name=%si Kutak
mcr_cnr.1.subset=Kutak
mcr_cnr.2.name=%si Ugao
mcr_cnr.2.subset=Ugao
mcr_cnr.4.name=%si Usjek
mcr_cnr.4.subset=Usjek
-32
View File
@@ -1,32 +0,0 @@
item.microblock:sawStone.name=Stenen Zaag
item.microblock:sawIron.name=IJzeren Zaag
item.microblock:sawDiamond.name=Diamanten Zaag
item.microblock:stoneRod.name=Stenen Stok
mcr_face.1.name=%s Bedekking
mcr_face.1.subset=Bedekking
mcr_face.2.name=%s Paneel
mcr_face.2.subset=Paneel
mcr_face.4.name=%s Plaat
mcr_face.4.subset=Plaat
mcr_hllw.1.name=Holle %s Bedekking
mcr_hllw.1.subset=Holle Bedekking
mcr_hllw.2.name=Hol %s Paneel
mcr_hllw.2.subset=Hol Paneel
mcr_hllw.4.name=Holle %s Plaat
mcr_hllw.4.subset=Holle Plaat
mcr_edge.1.name=%s Strook
mcr_edge.1.subset=Strook
mcr_edge.2.name=%s Paal
mcr_edge.2.subset=Paal
mcr_edge.4.name=%s Pilaar
mcr_edge.4.subset=Pilaar
mcr_cnr.1.name=%s Hoekje
mcr_cnr.1.subset=Hoekje
mcr_cnr.2.name=%s Hoek
mcr_cnr.2.subset=Hoek
mcr_cnr.4.name=%s Inkeping
mcr_cnr.4.subset=Inkeping
-35
View File
@@ -1,35 +0,0 @@
microblock.missing=Следующие микроблоки не установлены на этом клиенте: %s
multipart.missing=Следующие микроблоки из модов не установлены на этом клиенте: %s
item.microblock:sawStone.name=Каменная пила
item.microblock:sawIron.name=Железная пила
item.microblock:sawDiamond.name=Алмазная пила
item.microblock:stoneRod.name=Каменная палка
mcr_face.1.name=Крышка из %s
mcr_face.1.subset=Крышка
mcr_face.2.name=Панель из %s
mcr_face.2.subset=Панель
mcr_face.4.name=Плита из %s
mcr_face.4.subset=Плита
mcr_hllw.1.name=Крышка из %s с выемкой
mcr_hllw.1.subset=Крышка с выемкой
mcr_hllw.2.name=Панель из %s с выемкой
mcr_hllw.2.subset=Панель с выемкой
mcr_hllw.4.name=Плита из %s с выемкой
mcr_hllw.4.subset=Плита с выемкой
mcr_edge.1.name=Полоса из %s
mcr_edge.1.subset=Полоса
mcr_edge.2.name=Столб %s
mcr_edge.2.subset=Столб
mcr_edge.4.name=Колонна из %s
mcr_edge.4.subset=Колонна
mcr_cnr.1.name=Уголок из %s
mcr_cnr.1.subset=Уголок
mcr_cnr.2.name=Угол из %s
mcr_cnr.2.subset=Угол
mcr_cnr.4.name=Большой угол из %s
mcr_cnr.4.subset=Большой угол
-32
View File
@@ -1,32 +0,0 @@
item.microblock:sawStone.name=Taş Testere
item.microblock:sawIron.name=Demir Testere
item.microblock:sawDiamond.name=Elmas Testere
item.microblock:stoneRod.name=Taş Çubuk
mcr_face.1.name=%s Kapak
mcr_face.1.subset=Kapak
mcr_face.2.name=%s Panel
mcr_face.2.subset=Panel
mcr_face.4.name=Kalın %s
mcr_face.4.subset=Kalın
mcr_hllw.1.name=Ortası Boş %s Kapak
mcr_hllw.1.subset=Ortası Boş Cover
mcr_hllw.2.name=Ortası Boş %s Panel
mcr_hllw.2.subset=Ortası Boş Panel
mcr_hllw.4.name=Ortası Boş Kalın %s
mcr_hllw.4.subset=Ortası Boş Kalın
mcr_edge.1.name=%s Şerit
mcr_edge.1.subset=Şerit
mcr_edge.2.name=Kalın Şerit %s
mcr_edge.2.subset=Kalın Şerit
mcr_edge.4.name=%s Sütun
mcr_edge.4.subset=Sütun
mcr_cnr.1.name=%s Köşe
mcr_cnr.1.subset=Köşe
mcr_cnr.2.name=Orta Köşe %s
mcr_cnr.2.subset=Orta Köşe
mcr_cnr.4.name=Kalın Köşe %s
mcr_cnr.4.subset=Kalın Köşe
-32
View File
@@ -1,32 +0,0 @@
item.microblock:sawStone.name=石手锯
item.microblock:sawIron.name=铁手锯
item.microblock:sawDiamond.name=钻石手锯
item.microblock:stoneRod.name=石棍
mcr_face.1.name=%s 盖板
mcr_face.1.subset=盖板
mcr_face.2.name=%s 面板
mcr_face.2.subset=面板
mcr_face.4.name=%s 厚板
mcr_face.4.subset=厚板
mcr_hllw.1.name=%s 空心盖板
mcr_hllw.1.subset=空心盖板
mcr_hllw.2.name=%s 空心面板
mcr_hllw.2.subset=空心面板
mcr_hllw.4.name=%s 空心厚板
mcr_hllw.4.subset=空心厚板
mcr_edge.1.name=%s Strip
mcr_edge.1.subset=Strip
mcr_edge.2.name=%s Post
mcr_edge.2.subset=Post
mcr_edge.4.name=%s 柱
mcr_edge.4.subset=柱
mcr_cnr.1.name=%s Nook
mcr_cnr.1.subset=Nook
mcr_cnr.2.name=%s Corner
mcr_cnr.2.subset=Corner
mcr_cnr.4.name=%s Notch
mcr_cnr.4.subset=Notch
-173
View File
@@ -1,173 +0,0 @@
// This sets us up for building a forge project - you need all of these
buildscript {
repositories {
mavenCentral()
maven {
name = "forge"
url = "http://files.minecraftforge.net/maven"
}
maven {
name = "sonatype"
url = "https://oss.sonatype.org/content/repositories/snapshots/"
}
}
dependencies {
classpath 'net.minecraftforge.gradle:ForgeGradle:1.2-SNAPSHOT'
}
}
// Apply the forge plugin - this adds all the magic for automatically obfuscating, deobfuscating etc
apply plugin: 'forge'
apply plugin: 'scala'
// This is our group
group = "codechicken" // http://maven.apache.org/guides/mini/guide-naming-conventions.html
// This is our actual project within the group.
archivesBaseName = "ForgeMultipart"
// Define properties file
ext.configFile = file "build.properties"
configFile.withReader {
// Load config. It shall from now be referenced as simply config or project.config
def prop = new Properties()
prop.load(it)
project.ext.config = new ConfigSlurper().parse prop
}
dependencies {
compile "codechicken:CodeChickenLib:${config.mc_version}-${config.ccl_version}:dev"
}
version = "${project.config.mod_version}." + System.getenv("BUILD_NUMBER") ?: "1"
println config.mc_version + "-" + config.forge_version
// Setup the forge minecraft plugin data. Specify the preferred forge/minecraft version here
minecraft {
version = config.mc_version + "-" + config.forge_version
}
// this sets our output jar to have a 'tag' of 'universal' on it
// It also adds the minecraft version in a custom version name
// The result is files named <projectname>-<mcversion>-<version>-universal.jar
jar {
classifier = 'universal'
version = "${project.minecraft.version}-${project.version}"
manifest {
attributes 'FMLCorePlugin': 'codechicken.core.launch.DepLoader'
attributes 'FMLCorePluginContainsFMLMod': 'true'
}
}
sourceSets {
main {
scala {
srcDir project.projectDir
include 'codechicken/**/*.*'
}
resources {
srcDir project.projectDir
include 'assets/**/*.*'
include '*.info'
}
}
}
processResources {
// Replace properties in info files
from(sourceSets.main.resources.srcDirs) {
include '*.info'
expand 'version':version,'mc_version':config.mc_version,'ccl_version':config.ccl_version
}
// Copy everything else
from(sourceSets.main.resources.srcDirs) {
include 'assets/**/*.*'
}
}
// Add in a source jar for people, should they desire to download such a thing
task sourceJar(type: Jar) {
from sourceSets.main.allSource
classifier = 'src'
version = "${project.minecraft.version}-${project.version}"
}
// Add in an mcp named jar, for those who wish to run in a development environment (assuming mcp naming matches)
task deobfJar(type: Jar) {
from sourceSets.main.output
classifier = 'dev'
version = "${project.minecraft.version}-${project.version}"
manifest {
attributes 'FMLCorePlugin': 'codechicken.core.launch.DepLoader'
attributes 'FMLCorePluginContainsFMLMod': 'true'
}
}
// Tell the artifact system about our extra jars
artifacts {
archives sourceJar, deobfJar
}
// Configure an upload task. this is setup for uploading to files.minecraftforge.net. There are other examples around
uploadArchives {
dependsOn 'reobf'
repositories {
if (project.hasProperty("filesmaven")) {
logger.info('Publishing to files server')
mavenDeployer {
configuration = configurations.deployJars
repository(url: project.filesmaven.url) {
authentication(userName: project.filesmaven.username, privateKey: project.filesmaven.key)
}
// This is just the pom data for the maven repo
pom {
groupId = project.group
// Force the maven upload to use the <mcversion>-<version> syntax preferred at files
version = "${project.minecraft.version}-${project.version}"
artifactId = project.archivesBaseName
project {
name project.archivesBaseName
packaging 'jar'
description 'ForgeMultipart'
url 'https://github.com/Chicken-Bones/ForgeMultipart'
scm {
url 'https://github.com/Chicken-Bones/ForgeMultipart'
connection 'scm:git:git://github.com/Chicken-Bones/ForgeMultipart.git'
developerConnection 'scm:git:git@github.com:Chicken-Bones/ForgeMultipart.git'
}
issueManagement {
system 'github'
url 'https://github.com/Chicken-Bones/ForgeMultipart/issues'
}
licenses {
license {
name 'GNU Lesser Public License (GPL), Version 2.1'
url 'https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt'
distribution 'repo'
}
}
developers {
developer {
id 'chicken-bones'
name 'chicken-bones'
roles { role 'developer' }
}
}
}
}
}
} else {
logger.info('Publishing to repo folder')
mavenDeployer {
pom.version = "${project.minecraft.version}-${project.version}"
repository(url: 'file://localhost/' + project.file('repo').getAbsolutePath())
}
}
}
}
-4
View File
@@ -1,4 +0,0 @@
mc_version=1.7.2
forge_version=10.12.0.1054
ccl_version=1.1.0.71
mod_version=1.1.0
-609
View File
@@ -1,609 +0,0 @@
package codechicken.core.launch;
import java.awt.*;
import java.awt.Dialog.ModalityType;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.*;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLConnection;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import javax.swing.Box;
import javax.swing.JDialog;
import javax.swing.JEditorPane;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JProgressBar;
import javax.swing.WindowConstants;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import net.minecraft.launchwrapper.LaunchClassLoader;
import cpw.mods.fml.common.versioning.ComparableVersion;
import cpw.mods.fml.relauncher.FMLInjectionData;
import cpw.mods.fml.relauncher.FMLLaunchHandler;
import cpw.mods.fml.relauncher.IFMLCallHook;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
import sun.misc.URLClassPath;
import sun.net.util.URLUtil;
/**
* For autodownloading stuff.
* This is really unoriginal, mostly ripped off FML, credits to cpw.
*/
public class DepLoader implements IFMLLoadingPlugin, IFMLCallHook {
private static ByteBuffer downloadBuffer = ByteBuffer.allocateDirect(1 << 23);
private static final String owner = "CB's DepLoader";
private static DepLoadInst inst;
public interface IDownloadDisplay {
void resetProgress(int sizeGuess);
void setPokeThread(Thread currentThread);
void updateProgress(int fullLength);
boolean shouldStopIt();
void updateProgressString(String string, Object... data);
Object makeDialog();
void showErrorDialog(String name, String url);
}
@SuppressWarnings("serial")
public static class Downloader extends JOptionPane implements IDownloadDisplay {
private JDialog container;
private JLabel currentActivity;
private JProgressBar progress;
boolean stopIt;
Thread pokeThread;
private Box makeProgressPanel() {
Box box = Box.createVerticalBox();
box.add(Box.createRigidArea(new Dimension(0, 10)));
JLabel welcomeLabel = new JLabel("<html><b><font size='+1'>" + owner + " is setting up your minecraft environment</font></b></html>");
box.add(welcomeLabel);
welcomeLabel.setAlignmentY(LEFT_ALIGNMENT);
welcomeLabel = new JLabel("<html>Please wait, " + owner + " has some tasks to do before you can play</html>");
welcomeLabel.setAlignmentY(LEFT_ALIGNMENT);
box.add(welcomeLabel);
box.add(Box.createRigidArea(new Dimension(0, 10)));
currentActivity = new JLabel("Currently doing ...");
box.add(currentActivity);
box.add(Box.createRigidArea(new Dimension(0, 10)));
progress = new JProgressBar(0, 100);
progress.setStringPainted(true);
box.add(progress);
box.add(Box.createRigidArea(new Dimension(0, 30)));
return box;
}
@Override
public JDialog makeDialog() {
if (container != null)
return container;
setMessageType(JOptionPane.INFORMATION_MESSAGE);
setMessage(makeProgressPanel());
setOptions(new Object[]{"Stop"});
addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getSource() == Downloader.this && evt.getPropertyName() == VALUE_PROPERTY) {
requestClose("This will stop minecraft from launching\nAre you sure you want to do this?");
}
}
});
container = new JDialog(null, "Hello", ModalityType.MODELESS);
container.setResizable(false);
container.setLocationRelativeTo(null);
container.add(this);
this.updateUI();
container.pack();
container.setMinimumSize(container.getPreferredSize());
container.setVisible(true);
container.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
container.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
requestClose("Closing this window will stop minecraft from launching\nAre you sure you wish to do this?");
}
});
return container;
}
protected void requestClose(String message) {
int shouldClose = JOptionPane.showConfirmDialog(container, message, "Are you sure you want to stop?", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
if (shouldClose == JOptionPane.YES_OPTION)
container.dispose();
stopIt = true;
if (pokeThread != null)
pokeThread.interrupt();
}
@Override
public void updateProgressString(String progressUpdate, Object... data) {
//FMLLog.finest(progressUpdate, data);
if (currentActivity != null)
currentActivity.setText(String.format(progressUpdate, data));
}
@Override
public void resetProgress(int sizeGuess) {
if (progress != null)
progress.getModel().setRangeProperties(0, 0, 0, sizeGuess, false);
}
@Override
public void updateProgress(int fullLength) {
if (progress != null)
progress.getModel().setValue(fullLength);
}
@Override
public void setPokeThread(Thread currentThread) {
this.pokeThread = currentThread;
}
@Override
public boolean shouldStopIt() {
return stopIt;
}
@Override
public void showErrorDialog(String name, String url) {
JEditorPane ep = new JEditorPane("text/html",
"<html>" +
owner + " was unable to download required library " + name +
"<br>Check your internet connection and try restarting or download it manually from" +
"<br><a href=\"" + url + "\">" + url + "</a> and put it in your mods folder" +
"</html>");
ep.setEditable(false);
ep.setOpaque(false);
ep.addHyperlinkListener(new HyperlinkListener() {
@Override
public void hyperlinkUpdate(HyperlinkEvent event) {
try {
if (event.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED))
Desktop.getDesktop().browse(event.getURL().toURI());
} catch (Exception e) {
}
}
});
JOptionPane.showMessageDialog(null, ep, "A download error has occured", JOptionPane.ERROR_MESSAGE);
}
}
public static class DummyDownloader implements IDownloadDisplay {
@Override
public void resetProgress(int sizeGuess) {
}
@Override
public void setPokeThread(Thread currentThread) {
}
@Override
public void updateProgress(int fullLength) {
}
@Override
public boolean shouldStopIt() {
return false;
}
@Override
public void updateProgressString(String string, Object... data) {
}
@Override
public Object makeDialog() {
return null;
}
@Override
public void showErrorDialog(String name, String url) {
}
}
public static class Dependancy {
public String url;
public String[] filesplit;
public ComparableVersion version;
public String existing;
/**
* Flag set to add this dep to the classpath immediately because it is required for a coremod.
*/
public boolean coreLib;
public Dependancy(String url, String[] filesplit, boolean coreLib) {
this.url = url;
this.filesplit = filesplit;
this.coreLib = coreLib;
version = new ComparableVersion(filesplit[1]);
}
public String getName() {
return filesplit[0];
}
public String fileName() {
return filesplit[0] + filesplit[1] + filesplit[2];
}
}
public static class DepLoadInst {
private File modsDir;
private File v_modsDir;
private IDownloadDisplay downloadMonitor;
private JDialog popupWindow;
private Map<String, Dependancy> depMap = new HashMap<String, Dependancy>();
private HashSet<String> depSet = new HashSet<String>();
public DepLoadInst() {
String mcVer = (String) FMLInjectionData.data()[4];
File mcDir = (File) FMLInjectionData.data()[6];
modsDir = new File(mcDir, "mods");
v_modsDir = new File(mcDir, "mods/" + mcVer);
if (!v_modsDir.exists())
v_modsDir.mkdirs();
}
private void addClasspath(String name) {
try {
((LaunchClassLoader) DepLoader.class.getClassLoader()).addURL(new File(v_modsDir, name).toURI().toURL());
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
private void deleteMod(File mod) {
if (mod.delete())
return;
try {
ClassLoader cl = DepLoader.class.getClassLoader();
URL url = mod.toURI().toURL();
Field f_ucp = URLClassLoader.class.getDeclaredField("ucp");
Field f_loaders = URLClassPath.class.getDeclaredField("loaders");
Field f_lmap = URLClassPath.class.getDeclaredField("lmap");
f_ucp.setAccessible(true);
f_loaders.setAccessible(true);
f_lmap.setAccessible(true);
URLClassPath ucp = (URLClassPath) f_ucp.get(cl);
Closeable loader = ((Map<String, Closeable>) f_lmap.get(ucp)).remove(URLUtil.urlNoFragString(url));
if (loader != null) {
loader.close();
((List<?>) f_loaders.get(ucp)).remove(loader);
}
} catch (Exception e) {
e.printStackTrace();
}
if (!mod.delete()) {
mod.deleteOnExit();
String msg = owner + " was unable to delete file " + mod.getPath() + " the game will now try to delete it on exit. If this dialog appears again, delete it manually.";
System.err.println(msg);
if (!GraphicsEnvironment.isHeadless())
JOptionPane.showMessageDialog(null, msg, "An update error has occured", JOptionPane.ERROR_MESSAGE);
System.exit(1);
}
}
private void download(Dependancy dep) {
popupWindow = (JDialog) downloadMonitor.makeDialog();
File libFile = new File(v_modsDir, dep.fileName());
try {
URL libDownload = new URL(dep.url + '/' + dep.fileName());
downloadMonitor.updateProgressString("Downloading file %s", libDownload.toString());
System.out.format("Downloading file %s\n", libDownload.toString());
URLConnection connection = libDownload.openConnection();
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
connection.setRequestProperty("User-Agent", "" + owner + " Downloader");
int sizeGuess = connection.getContentLength();
download(connection.getInputStream(), sizeGuess, libFile);
downloadMonitor.updateProgressString("Download complete");
System.out.println("Download complete");
scanDepInfo(libFile);
} catch (Exception e) {
libFile.delete();
if (downloadMonitor.shouldStopIt()) {
System.err.println("You have stopped the downloading operation before it could complete");
System.exit(1);
return;
}
downloadMonitor.showErrorDialog(dep.fileName(), dep.url + '/' + dep.fileName());
throw new RuntimeException("A download error occured", e);
}
}
private void download(InputStream is, int sizeGuess, File target) throws Exception {
if (sizeGuess > downloadBuffer.capacity())
throw new Exception(String.format("The file %s is too large to be downloaded by " + owner + " - the download is invalid", target.getName()));
downloadBuffer.clear();
int bytesRead, fullLength = 0;
downloadMonitor.resetProgress(sizeGuess);
try {
downloadMonitor.setPokeThread(Thread.currentThread());
byte[] smallBuffer = new byte[1024];
while ((bytesRead = is.read(smallBuffer)) >= 0) {
downloadBuffer.put(smallBuffer, 0, bytesRead);
fullLength += bytesRead;
if (downloadMonitor.shouldStopIt()) {
break;
}
downloadMonitor.updateProgress(fullLength);
}
is.close();
downloadMonitor.setPokeThread(null);
downloadBuffer.limit(fullLength);
downloadBuffer.position(0);
} catch (InterruptedIOException e) {
// We were interrupted by the stop button. We're stopping now.. clear interruption flag.
Thread.interrupted();
throw new Exception("Stop");
} catch (IOException e) {
throw e;
}
try {
/*String cksum = generateChecksum(downloadBuffer);
if (cksum.equals(validationHash))
{*/
if (!target.exists())
target.createNewFile();
downloadBuffer.position(0);
FileOutputStream fos = new FileOutputStream(target);
fos.getChannel().write(downloadBuffer);
fos.close();
/*}
else
{
throw new RuntimeException(String.format("The downloaded file %s has an invalid checksum %s (expecting %s). The download did not succeed correctly and the file has been deleted. Please try launching again.", target.getName(), cksum, validationHash));
}*/
} catch (Exception e) {
throw e;
}
}
private String checkExisting(String[] dependency) {
for (File f : modsDir.listFiles()) {
String[] split = splitFileName(f.getName());
if (split == null || !split[0].equals(dependency[0]))
continue;
if (f.renameTo(new File(v_modsDir, f.getName())))
continue;
deleteMod(f);
}
for (File f : v_modsDir.listFiles()) {
String[] split = splitFileName(f.getName());
if (split == null || !split[0].equals(dependency[0]))
continue;
ComparableVersion found = new ComparableVersion(split[1]);
ComparableVersion requested = new ComparableVersion(dependency[1]);
int cmp = found.compareTo(requested);
if (cmp < 0) {
System.out.println("Deleted old version " + f.getName());
deleteMod(f);
return null;
}
if (cmp > 0) {
System.err.println("Warning: version of " + dependency[0] + ", " + split[1] + " is newer than request " + dependency[1]);
return f.getName();
}
return f.getName();//found dependency
}
return null;
}
public void load() {
scanDepInfos();
if (depMap.isEmpty())
return;
loadDeps();
activateDeps();
}
private void activateDeps() {
for (Dependancy dep : depMap.values())
if (dep.coreLib)
addClasspath(dep.existing);
}
private void loadDeps() {
downloadMonitor = FMLLaunchHandler.side().isClient() ? new Downloader() : new DummyDownloader();
try {
while (!depSet.isEmpty()) {
Iterator<String> it = depSet.iterator();
Dependancy dep = depMap.get(it.next());
it.remove();
load(dep);
}
} finally {
if (popupWindow != null) {
popupWindow.setVisible(false);
popupWindow.dispose();
}
}
}
private void load(Dependancy dep) {
dep.existing = checkExisting(dep.filesplit);
if (dep.existing == null)//download dep
{
download(dep);
dep.existing = dep.fileName();
}
}
private List<File> modFiles() {
List<File> list = new LinkedList<File>();
list.addAll(Arrays.asList(modsDir.listFiles()));
list.addAll(Arrays.asList(v_modsDir.listFiles()));
return list;
}
private void scanDepInfos() {
for (File file : modFiles()) {
if (!file.getName().endsWith(".jar") && !file.getName().endsWith(".zip"))
continue;
scanDepInfo(file);
}
}
private void scanDepInfo(File file) {
try {
ZipFile zip = new ZipFile(file);
ZipEntry e = zip.getEntry("dependancies.info");
if (e == null) e = zip.getEntry("dependencies.info");
if (e != null)
loadJSon(zip.getInputStream(e));
zip.close();
} catch (Exception e) {
System.err.println("Failed to load dependencies.info from " + file.getName() + " as JSON");
e.printStackTrace();
}
}
private void loadJSon(InputStream input) throws IOException {
InputStreamReader reader = new InputStreamReader(input);
JsonElement root = new JsonParser().parse(reader);
if (root.isJsonArray())
loadJSonArr(root);
else
loadJson(root.getAsJsonObject());
reader.close();
}
private void loadJSonArr(JsonElement root) throws IOException {
for (JsonElement node : root.getAsJsonArray())
loadJson(node.getAsJsonObject());
}
private void loadJson(JsonObject node) throws IOException {
boolean obfuscated = ((LaunchClassLoader) DepLoader.class.getClassLoader())
.getClassBytes("net.minecraft.world.World") == null;
String testClass = node.get("class").getAsString();
if (DepLoader.class.getResource("/" + testClass.replace('.', '/') + ".class") != null)
return;
String repo = node.get("repo").getAsString();
String file = node.get("file").getAsString();
if (!obfuscated && node.has("dev"))
file = node.get("dev").getAsString();
boolean coreLib = node.has("coreLib") && node.get("coreLib").getAsBoolean();
String[] split = splitFileName(file);
if (split == null)
throw new RuntimeException("Invalid filename format for dependency: " + file);
addDep(new Dependancy(repo, split, coreLib));
}
private void addDep(Dependancy newDep) {
if (mergeNew(depMap.get(newDep.getName()), newDep)) {
depMap.put(newDep.getName(), newDep);
depSet.add(newDep.getName());
}
}
private boolean mergeNew(Dependancy oldDep, Dependancy newDep) {
if (oldDep == null)
return true;
Dependancy newest = newDep.version.compareTo(oldDep.version) > 0 ? newDep : oldDep;
newest.coreLib = newDep.coreLib || oldDep.coreLib;
return newest == newDep;
}
}
public static void load() {
if (inst == null) {
inst = new DepLoadInst();
inst.load();
}
}
private static String[] splitFileName(String filename) {
Pattern p = Pattern.compile("(.+?)([\\d\\.\\w]+)(\\.[^\\d]+)");
Matcher m = p.matcher(filename);
if (!m.matches())
return null;
return new String[]{m.group(1), m.group(2), m.group(3)};
}
@Override
public String[] getASMTransformerClass() {
return null;
}
@Override
public String getModContainerClass() {
return null;
}
@Override
public String getSetupClass() {
return getClass().getName();
}
@Override
public void injectData(Map<String, Object> data) {
}
@Override
public Void call() {
load();
return null;
}
@Override
public String getAccessTransformerClass() {
return null;
}
}
@@ -1,145 +0,0 @@
package codechicken.microblock
import net.minecraft.block.Block
import codechicken.microblock.MicroMaterialRegistry.IMicroMaterial
import net.minecraft.util.IIcon
import cpw.mods.fml.relauncher.SideOnly
import cpw.mods.fml.relauncher.Side
import net.minecraft.item.ItemStack
import codechicken.lib.vec.{Cuboid6, Vector3}
import net.minecraft.entity.player.EntityPlayer
import codechicken.lib.render.{CCRenderPipeline, ColourMultiplier, CCRenderState}
import codechicken.microblock.handler.MicroblockProxy
import net.minecraft.entity.Entity
import codechicken.lib.render.uv.{UVTransformation, MultiIconTransformation}
object MaterialRenderHelper
{
private var pass = 0
private var builder:CCRenderPipeline#PipelineBuilder = _
def start(pos:Vector3, pass:Int, uvt:UVTransformation) = {
this.pass = pass
builder = CCRenderState.pipeline.builder()
builder.add(pos.translation()).add(uvt)
this
}
def blockColour(colour:Int) = {
builder.add(ColourMultiplier.instance(colour))
this
}
def lighting() = {
if(pass != -1)
builder.add(CCRenderState.lightMatrix)
this
}
def render() = builder.render()
}
/**
* Standard micro material class suitable for most blocks.
*/
class BlockMicroMaterial(val block:Block, val meta:Int = 0) extends IMicroMaterial
{
val blockKey = Block.blockRegistry.getNameForObject(block)
@SideOnly(Side.CLIENT)
var icont:MultiIconTransformation = _
@SideOnly(Side.CLIENT)
override def loadIcons() {
def safeIcon(block:Block, side:Int):IIcon = {
try {
return MicroblockProxy.renderBlocks.getIconSafe(block.getIcon(side, meta))
}
catch {
case e:Exception =>
}
return MicroblockProxy.renderBlocks.getIconSafe(null)
}
val iblock = Block.getBlockFromName(blockKey)//reacquire block instance incase a mod replcaed it
icont = new MultiIconTransformation(Array.tabulate(6)(side => safeIcon(iblock, side)):_*)
}
def renderMicroFace(pos:Vector3, pass:Int, bounds:Cuboid6) {
MaterialRenderHelper.start(pos, pass, icont).blockColour(getColour(pass)).lighting().render()
}
def getColour(pass:Int) = {
if(pass == -1)
block.getBlockColor<<8|0xFF
else {
val pos = CCRenderState.lightMatrix.pos
block.colorMultiplier(CCRenderState.lightMatrix.access, pos.x, pos.y, pos.z)<<8|0xFF
}
}
override def canRenderInPass(pass:Int) = block.canRenderInPass(pass)
@SideOnly(Side.CLIENT)
def getBreakingIcon(side:Int) = block.getIcon(side, meta)
def getItem = new ItemStack(block, 1, meta)
def getLocalizedName = getItem.getDisplayName
def getStrength(player:EntityPlayer) = {
var hardness = 30F
try {
hardness = block.getBlockHardness(null, 0, 0, 0)
} catch {
case e:Exception =>
}
player.getBreakSpeed(block, false, meta) / hardness
}
def isTransparent = !block.isOpaqueCube
def getLightValue = block.getLightValue
def toolClasses = Seq("axe", "pickaxe", "shovel")
def getCutterStrength = block.getHarvestLevel(meta)
def getSound = block.stepSound
def explosionResistance(entity:Entity):Float = block.getExplosionResistance(entity)
}
/**
* Utility functions for cleaner registry code
*/
object BlockMicroMaterial
{
def materialKey(block:Block, meta:Int):String = materialKey(block.getUnlocalizedName, meta)
def materialKey(name:String, meta:Int) = name+(if(meta > 0) "_"+meta else "")
def createAndRegister(block:Block, name:String)
{
createAndRegister(block, 0, name)
}
def createAndRegister(block:Block, meta:Int = 0)
{
MicroMaterialRegistry.registerMaterial(new BlockMicroMaterial(block, meta), materialKey(block, meta))
}
def createAndRegister(block:Block, meta:Seq[Int])
{
meta.foreach(m => createAndRegister(block, m))
}
def createAndRegister(block:Block, meta:Int, name:String)
{
MicroMaterialRegistry.registerMaterial(new BlockMicroMaterial(block, meta), materialKey(name, meta))
}
def createAndRegister(block:Block, meta:Seq[Int], name:String)
{
meta.foreach(m => createAndRegister(block, m, name))
}
}
@@ -1,208 +0,0 @@
package codechicken.microblock
import net.minecraft.nbt.NBTTagCompound
import codechicken.lib.lighting.LightMatrix
import codechicken.microblock.MicroMaterialRegistry._
import codechicken.lib.vec.BlockCoord
import org.lwjgl.opengl.GL11
import codechicken.lib.render.CCRenderState
import net.minecraft.world.World
import codechicken.lib.vec.Cuboid6
import codechicken.lib.vec.Vector3
import codechicken.multipart.TIconHitEffects
import net.minecraft.util.IIcon
import net.minecraft.block.Block
import codechicken.multipart.TCuboidPart
import net.minecraft.util.MovingObjectPosition
import net.minecraft.entity.player.EntityPlayer
import codechicken.multipart.JPartialOcclusion
import codechicken.lib.render.Vertex5
import net.minecraft.item.ItemStack
import scala.collection.mutable.ListBuffer
import codechicken.lib.data.MCDataOutput
import codechicken.lib.data.MCDataInput
import codechicken.lib.render.RenderUtils
import codechicken.lib.render.IFaceRenderer
import codechicken.multipart.TSlottedPart
import scala.collection.JavaConversions._
import codechicken.lib.render.TextureUtils
import net.minecraft.entity.Entity
import net.minecraft.init.Blocks
import cpw.mods.fml.relauncher.{Side, SideOnly}
import codechicken.lib.render.BlockRenderer.BlockFace
object MicroblockRender
{
def renderHighlight(player:EntityPlayer, hit:MovingObjectPosition, mcrClass:MicroblockClass, size:Int, material:Int)
{
mcrClass.placementProperties.placementGrid.render(new Vector3(hit.hitVec), hit.sideHit)
val placement = MicroblockPlacement(player, hit, size, material, !player.capabilities.isCreativeMode, mcrClass.placementProperties)
if(placement == null)
return
val pos = placement.pos
val part = placement.part.asInstanceOf[MicroblockClient]
GL11.glPushMatrix()
GL11.glTranslated(pos.x+0.5, pos.y+0.5, pos.z+0.5)
GL11.glScaled(1.002, 1.002, 1.002)
GL11.glTranslated(-0.5, -0.5, -0.5)
GL11.glEnable(GL11.GL_BLEND)
GL11.glDepthMask(false)
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA)
TextureUtils.bindAtlas(0)
CCRenderState.reset()
CCRenderState.alphaOverride = 80
CCRenderState.useNormals = true
CCRenderState.startDrawing()
part.render(Vector3.zero, -1)
CCRenderState.draw()
GL11.glDisable(GL11.GL_BLEND)
GL11.glDepthMask(true)
GL11.glPopMatrix()
}
val face = new BlockFace()
def renderCuboid(pos:Vector3, mat:IMicroMaterial, pass:Int, c:Cuboid6, faces:Int) {
CCRenderState.setModel(face)
for(s <- 0 until 6 if (faces & 1<<s) == 0) {
face.loadCuboidFace(c, s)
mat.renderMicroFace(pos, pass, c)
}
}
}
trait MicroblockClient extends Microblock with TIconHitEffects with IMicroMaterialRender
{
def getBrokenIcon(side:Int) = getIMaterial match {
case null => Blocks.stone.getIcon(0, 0)
case mat => mat.getBreakingIcon(side)
}
override def renderStatic(pos:Vector3, pass:Int) = {
if(getIMaterial.canRenderInPass(pass)) {
render(pos, pass)
true
}
else
false
}
def render(pos:Vector3, pass:Int)
override def getRenderBounds = getBounds
}
abstract class Microblock(var shape:Byte = 0, var material:Int = 0) extends TCuboidPart
{
def this(size:Int, shape:Int, material:Int) = this((size<<4|shape).toByte, material)
override def getStrength(hit:MovingObjectPosition, player:EntityPlayer) = getIMaterial match {
case null => super.getStrength(hit, player)
case mat => mat.getStrength(player)
}
override def doesTick = false
def getSize = shape>>4
def getShape = shape&0xF
def getMaterial = material
def getIMaterial = MicroMaterialRegistry.getMaterial(material)
def itemClassID:Int = -1
override def getDrops =
{
var size = getSize
val items = ListBuffer[ItemStack]()
for(s <- Seq(4, 2, 1))
{
val m = size/s
size-=m*s
if(m > 0)
items+=ItemMicroPart.create(m, s|itemClassID<<8, MicroMaterialRegistry.materialName(material))
}
items
}
override def pickItem(hit:MovingObjectPosition):ItemStack =
{
val size = getSize
for(s <- Seq(4, 2, 1))
if(size%s == 0 && size/s >= 1)
return ItemMicroPart.create(s|itemClassID<<8, MicroMaterialRegistry.materialName(material))
return null//unreachable
}
override def writeDesc(packet:MCDataOutput)
{
packet.writeByte(shape)
writeMaterialID(packet, material)
}
override def readDesc(packet:MCDataInput)
{
shape = packet.readByte
material = readMaterialID(packet)
}
override def read(packet:MCDataInput)
{
readDesc(packet)
tile.notifyPartChange(this)
tile.markRender()
}
override def save(tag:NBTTagCompound)
{
tag.setByte("shape", shape)
tag.setString("material", materialName(material))
}
override def load(tag:NBTTagCompound)
{
shape = tag.getByte("shape")
material = materialID(tag.getString("material"))
}
def isTransparent = getIMaterial.isTransparent
override def getLightValue = getIMaterial.getLightValue
def getResistanceFactor:Float
override def explosionResistance(entity:Entity) = getIMaterial.explosionResistance(entity)*getResistanceFactor
}
trait CommonMicroblockClient extends CommonMicroblock with MicroblockClient with TMicroOcclusionClient
{
def render(pos:Vector3, pass:Int) {
if(pass < 0)
MicroblockRender.renderCuboid(pos, getIMaterial, pass, getBounds, 0)
else
MicroblockRender.renderCuboid(pos, getIMaterial, pass, renderBounds, renderMask)
}
}
abstract class CommonMicroblock(shape$:Byte = 0, material$:Int = 0) extends Microblock(shape$, material$) with JPartialOcclusion with TMicroOcclusion with TSlottedPart
{
def microClass:MicroblockClass
def getType = microClass.getName
def getSlot = getShape
def getSlotMask = 1<<getSlot
def getPartialOcclusionBoxes = Seq(getBounds)
override def itemClassID = microClass.classID
def getResistanceFactor = microClass.getResistanceFactor
}
-186
View File
@@ -1,186 +0,0 @@
package codechicken.microblock
import java.io.File
import java.io.IOException
import java.io.PrintWriter
import java.io.BufferedReader
import java.io.FileReader
import scala.collection.mutable.{Map => MMap}
import net.minecraft.block.Block
import net.minecraft.item.Item
import java.lang.Exception
import cpw.mods.fml.common.event.FMLInterModComms.IMCMessage
import net.minecraft.item.ItemStack
import BlockMicroMaterial.createAndRegister
import BlockMicroMaterial.materialKey
import java.util.ArrayList
import scala.collection.JavaConversions._
import net.minecraft.init.Blocks
object ConfigContent
{
private val nameMap = MMap[String, Seq[Int]]()
private val idMap = new ArrayList[Seq[Int]]()
def parse(cfgDir:File)
{
val cfgFile = new File(cfgDir, "microblocks.cfg")
try
{
if(!cfgFile.exists())
generateDefault(cfgFile)
else
loadLines(cfgFile)
}
catch
{
case e:IOException => e.printStackTrace()
}
}
def generateDefault(cfgFile:File)
{
val writer = new PrintWriter(cfgFile)
writer.println("#Configuration file for adding microblock materials for aesthetic blocks added by mods")
writer.println("#Each line needs to be of the form <ID>:<meta>")
writer.println("#<ID> may be a numerical id or the unlocalised name of the block/item enclosed in double quotes. NEI can help you find these")
writer.println("#<meta> may be ommitted, in which case it defaults to 0, otherwise it can be a number, a comma separated list of numbers, or a dash separated range")
writer.println("#Ex. 3. 5:3. 213:1,2,3,5. \"tile.blockIron\":0-15")
writer.close()
}
def loadLine(line:String)
{
if(line.startsWith("#"))
return
var split1 = line.replace(" ", "").split(":")
if(split1.length > 2)
throw new IllegalArgumentException("Invalid number of ':' separators")
if(split1.length == 1)
split1 = Array(split1(0), "0")
if(split1(0) == "")
return
val split2 = split1(1).split(",")
val meta = split2.flatMap{s =>
if(s.contains("-"))
{
val split2 = s.split("-")
if(split2.length != 2)
throw new IllegalArgumentException("Invalid - separated range")
split2(0).toInt to split2(1).toInt
}
else
{
Seq(s.toInt)
}
}
val s_id = split1(0)
if(s_id.charAt(0) == '\"')
{
if(!s_id.endsWith("\""))
throw new IllegalArgumentException("Missing closing \" character")
val name = s_id.substring(1, s_id.length-1)
nameMap.put(name, meta)
}
else
{
idMap(s_id.toInt) = meta
}
}
def loadLines(cfgFile:File)
{
val reader = new BufferedReader(new FileReader(cfgFile))
var s:String = null
do
{
s = reader.readLine
if(s != null)
{
try
{
loadLine(s)
}
catch
{
case e:Exception =>
System.err.println("Invalid line in microblocks.cfg: "+s)
System.err.println(e.getMessage)
}
}
}
while(s != null)
reader.close()
}
def load()
{
for(i <- 0 until idMap.size)
{
val block = Block.getBlockById(i)
if(idMap(i) != null)
{
if(block == Blocks.air)
System.err.println("Warning: Unable to add micro material for block with ID "+i+" as it doesn't exist")
else
createAndRegister(block, idMap(i))
}
else if(block != null)
{
val name = block.getUnlocalizedName
val value = nameMap.get(name)
if(value.isDefined)
{
nameMap.remove(name)
value.get.foreach{m =>
try {
createAndRegister(block, m)
}
catch {
case e:IllegalStateException => System.err.println("Unable to register micro material: "+
materialKey(block, m)+"\n\t"+e.getMessage)
case e:Exception =>
System.err.println("Unable to register micro material: "+materialKey(block, m))
e.printStackTrace()
}
}
}
}
}
nameMap.foreach(e => System.err.println("Warning: Unable to add micro material for block with unlocalised name "+e._1+" as it doesn't exist"))
}
def handleIMC(messages:Seq[IMCMessage]) {
messages.filter(_.key == "microMaterial").foreach{msg =>
def error(s:String) {
System.err.println("Invalid microblock IMC message from "+msg.getSender+": "+s)
}
if(msg.getMessageType != classOf[ItemStack])
error("value is not an instanceof ItemStack")
else {
val stack = msg.getItemStackValue
if(Block.blockRegistry.containsId(Item.getIdFromItem(stack.getItem)))
System.err.println("Invalid Block: "+stack.getItem)
else if(stack.getItemDamage < 0 || stack.getItemDamage >= 16)
System.err.println("Invalid metadata: "+stack.getItemDamage)
else {
try {
createAndRegister(Block.getBlockFromItem(stack.getItem), stack.getItemDamage)
}
catch {
case e:IllegalStateException => System.err.println("Unable to register micro material: "+
materialKey(Block.getBlockFromItem(stack.getItem), stack.getItemDamage)+"\n\t"+e.getMessage)
}
}
}
}
}
}
@@ -1,70 +0,0 @@
package codechicken.microblock
import codechicken.lib.vec.Cuboid6
import codechicken.lib.vec.Scale
import codechicken.lib.vec.Vector3
import Vector3._
object CornerPlacement extends PlacementProperties
{
def microClass = CornerMicroClass
def placementGrid = CornerPlacementGrid
def opposite(slot:Int, side:Int) = ((slot-7)^(1<<(side>>1)))+7
}
object CornerMicroClass extends MicroblockClass
{
var aBounds:Array[Cuboid6] = new Array(256)
for(s <- 0 until 8)
{
val rx = if((s&4) != 0) -1 else 1
val ry = if((s&1) != 0) -1 else 1
val rz = if((s&2) != 0) -1 else 1
val transform = new Scale(new Vector3(rx, ry, rz)).at(center)
for(t <- 1 until 8)
{
val d = t/8D
aBounds(t<<4|s) = new Cuboid6(0, 0, 0, d, d, d).apply(transform)
}
}
override def itemSlot = 7
def getName = "mcr_cnr"
def create(client:Boolean) =
if(client)
new CornerMicroblockClient
else
new CornerMicroblock
def create(size:Int, slot:Int, material:Int, client:Boolean) =
if(client)
new CornerMicroblockClient(size, slot, material)
else
new CornerMicroblock(size, slot, material)
def placementProperties = CornerPlacement
def getResistanceFactor = 1
}
class CornerMicroblockClient(shape$:Byte = 0, material$:Int = 0) extends CornerMicroblock(shape$, material$) with CommonMicroblockClient
{
def this(size:Int, slot:Int, material:Int) = this((size<<4|(slot-7)).toByte, material)
}
class CornerMicroblock(shape$:Byte = 0, material$:Int = 0) extends CommonMicroblock(shape$, material$)
{
def this(size:Int, slot:Int, material:Int) = this((size<<4|(slot-7)).toByte, material)
def microClass = CornerMicroClass
def getBounds = CornerMicroClass.aBounds(shape)
override def getSlot = getShape+7
}
@@ -1,56 +0,0 @@
package codechicken.microblock
import net.minecraft.init.Blocks._
import BlockMicroMaterial._
import MicroMaterialRegistry._
object DefaultContent
{
def load()
{
FaceMicroClass.register(0)
HollowMicroClass.register(1)
CornerMicroClass.register(2)
EdgeMicroClass.register(3)
PostMicroClass.register()
createAndRegister(stone)
createAndRegister(dirt)
createAndRegister(cobblestone)
createAndRegister(planks, 0 to 3)
createAndRegister(log, 0 to 3)
createAndRegister(log2, 0 to 1, "tile.log2")
createAndRegister(leaves, 0 to 3)
createAndRegister(sponge)
createAndRegister(glass)
createAndRegister(lapis_block)
createAndRegister(sandstone, 0 to 2)
createAndRegister(wool, 0 to 15)
createAndRegister(gold_block)
createAndRegister(iron_block)
createAndRegister(brick_block)
createAndRegister(bookshelf)
createAndRegister(mossy_cobblestone)
createAndRegister(obsidian)
createAndRegister(diamond_block)
createAndRegister(ice)
createAndRegister(snow)
createAndRegister(clay)
createAndRegister(netherrack)
createAndRegister(soul_sand)
createAndRegister(glowstone)
createAndRegister(stonebrick, 0 to 3)
createAndRegister(nether_brick)
createAndRegister(end_stone)
createAndRegister(emerald_block)
createAndRegister(redstone_block)
createAndRegister(quartz_block)
createAndRegister(stained_hardened_clay, 0 to 15)
createAndRegister(hardened_clay)
createAndRegister(coal_block)
createAndRegister(packed_ice)
createAndRegister(stained_glass)
registerMaterial(new GrassMicroMaterial, grass.getUnlocalizedName)
registerMaterial(new TopMicroMaterial(mycelium), mycelium.getUnlocalizedName)
}
}
-264
View File
@@ -1,264 +0,0 @@
package codechicken.microblock
import codechicken.lib.vec.Cuboid6
import codechicken.lib.vec.Scale
import codechicken.lib.vec.Vector3
import codechicken.lib.vec.TransformationList
import codechicken.lib.vec.AxisCycle
import codechicken.lib.vec.Rotation
import Vector3._
import Rotation._
import codechicken.multipart.TMultiPart
import codechicken.multipart.TNormalOcclusion
import codechicken.multipart.JPartialOcclusion
import codechicken.multipart.TileMultipart
import codechicken.multipart.MultiPartRegistry
import codechicken.microblock.MicroMaterialRegistry.IMicroMaterial
import codechicken.lib.raytracer.ExtendedMOP
import codechicken.multipart.TEdgePart
import scala.collection.JavaConversions._
import codechicken.lib.data.MCDataInput
import codechicken.lib.lighting.LightMatrix
object EdgePlacement extends PlacementProperties
{
import codechicken.multipart.PartMap._
def microClass = EdgeMicroClass
def placementGrid = EdgePlacementGrid
def opposite(slot:Int, side:Int):Int =
{
if(slot < 0)//custom placement
return slot
val e = slot-15
return 15+ packEdgeBits(e, unpackEdgeBits(e)^(1<<(side>>1)))
}
override def customPlacement(pmt:MicroblockPlacement):ExecutablePlacement =
{
if(pmt.size%2 == 1) return null
val part = PostMicroClass.create(pmt.size, pmt.hit.sideHit>>1, pmt.material, pmt.world.isRemote)
if(pmt.doExpand)
{
val hpart = pmt.htile.partList(ExtendedMOP.getData[(Int, _)](pmt.hit)._1)
if(hpart.getType == PostMicroClass.getName)
{
val mpart = hpart.asInstanceOf[Microblock]
if(mpart.material == pmt.material && mpart.getSize + pmt.size < 8)
{
part.shape = ((mpart.getSize+pmt.size)<<4|mpart.getShape).toByte
return pmt.expand(mpart, part)
}
}
}
if(pmt.slot >= 0)
return null
if(pmt.internal && !pmt.oppMod)
return pmt.internalPlacement(pmt.htile.asInstanceOf[TileMultipart], part)
return pmt.externalPlacement(part)
}
}
object EdgeMicroClass extends MicroblockClass
{
var aBounds:Array[Cuboid6] = new Array(256)
for(s <- 0 until 12)
{
val rx = if((s&2) != 0) -1 else 1
val rz = if((s&1) != 0) -1 else 1
val transform = new TransformationList(new Scale(new Vector3(rx, 1, rz)), AxisCycle.cycles(s>>2)).at(center)
for(t <- 1 until 8)
{
val d = t/8D
aBounds(t<<4|s) = new Cuboid6(0, 0, 0, d, 1, d).apply(transform)
}
}
override def itemSlot = 15
def getName = "mcr_edge"
def create(client:Boolean) =
if(client)
new EdgeMicroblockClient
else
new EdgeMicroblock
def create(size:Int, slot:Int, material:Int, client:Boolean) =
if(client)
new EdgeMicroblockClient(size, slot, material)
else
new EdgeMicroblock(size, slot, material)
def placementProperties = EdgePlacement
def getResistanceFactor = 0.5F
}
class EdgeMicroblock(shape$:Byte = 0, material$:Int = 0) extends CommonMicroblock(shape$, material$) with TEdgePart
{
def this(size:Int, slot:Int, material:Int) = this((size<<4|(slot-15)).toByte, material)
def microClass = EdgeMicroClass
def getBounds = EdgeMicroClass.aBounds(shape)
override def getSlot = getShape+15
}
class EdgeMicroblockClient(shape$:Byte = 0, material$:Int = 0) extends EdgeMicroblock(shape$, material$) with CommonMicroblockClient
{
def this(size:Int, slot:Int, material:Int) = this((size<<4|(slot-15)).toByte, material)
}
object PostMicroClass
{
var aBounds:Array[Cuboid6] = new Array(256)
for(s <- 0 until 3)
{
val transform = sideRotations(s<<1).at(center)
for(t <- 2 until 8 by 2)
{
val d1 = 0.5-t/16D
val d2 = 0.5+t/16D
aBounds(t<<4|s) = new Cuboid6(d1, 0, d1, d2, 1, d2).apply(transform)
}
}
def getName = "mcr_post"
def create(client:Boolean) =
if(client)
new PostMicroblockClient
else
new PostMicroblock
def create(size:Int, slot:Int, material:Int, client:Boolean) =
if(client)
new PostMicroblockClient(size, slot, material)
else
new PostMicroblock(size, slot, material)
def register() = MultiPartRegistry.registerParts((_, c:Boolean) => create(c), getName)
def getResistanceFactor = 0.5F
}
class PostMicroblockClient(shape$:Byte = 0, material$:Int = 0) extends PostMicroblock(shape$, material$) with MicroblockClient
{
var renderBounds1:Cuboid6 = _
var renderBounds2:Cuboid6 = _
def this(size:Int, orient:Int, material:Int) = this((size<<4|orient).toByte, material)
override def render(pos:Vector3, pass:Int)
{
val mat = getIMaterial
if(pass == -1)
MicroblockRender.renderCuboid(pos, mat, pass, getBounds, 0)
else {
MicroblockRender.renderCuboid(pos, mat, pass, renderBounds1, 0)
if (renderBounds2 != null)
MicroblockRender.renderCuboid(pos, mat, pass, renderBounds2, 0)
}
}
override def onPartChanged(part:TMultiPart)
{
recalcBounds()
}
override def onAdded()
{
super.onAdded()
recalcBounds()
}
override def read(packet:MCDataInput)
{
super.read(packet)
recalcBounds()
}
def recalcBounds()
{
renderBounds1 = getBounds.copy
renderBounds2 = null
shrinkFace(getShape<<1)
shrinkFace(getShape<<1|1)
tile.partList.foreach(p =>
if(p.isInstanceOf[PostMicroblock] && p != this)
shrinkPost(p.asInstanceOf[PostMicroblock])
)
}
def shrinkFace(fside:Int)
{
val part = tile.partMap(fside)
if(part != null && part.getType.equals("mcr_face"))
MicroOcclusion.shrink(renderBounds1, part.asInstanceOf[CommonMicroblock].getBounds, fside)
}
def shrinkPost(post:PostMicroblock)
{
if(post == this)
return
if(thisShrinks(post))
{
if(renderBounds2 == null)
renderBounds2 = renderBounds1.copy
else
renderBounds2.set(renderBounds1)
MicroOcclusion.shrink(renderBounds1, post.getBounds, getShape<<1|1)
MicroOcclusion.shrink(renderBounds2, post.getBounds, getShape<<1)
}
}
def thisShrinks(other:PostMicroblock):Boolean =
{
if(getSize != other.getSize) return getSize < other.getSize
if(isTransparent != other.isTransparent) return isTransparent
return getShape > other.getShape
}
}
class PostMicroblock(shape$:Byte = 0, material$:Int = 0) extends Microblock(shape$, material$) with JPartialOcclusion with TNormalOcclusion
{
def this(size:Int, orient:Int, material:Int) = this((size<<4|orient).toByte, material)
override def getType = PostMicroClass.getName
def getBounds = PostMicroClass.aBounds(shape)
def getOcclusionBoxes = Seq(getBounds)
def getPartialOcclusionBoxes = getOcclusionBoxes
override def itemClassID = EdgeMicroClass.classID
override def occlusionTest(npart:TMultiPart): Boolean =
{
if(npart.isInstanceOf[PostMicroblock])
return npart.asInstanceOf[PostMicroblock].getShape != getShape
if(npart.getType.equals("mcr_face"))
if(npart.asInstanceOf[CommonMicroblock].getSlot>>1 == getShape)
return true
return super.occlusionTest(npart)
}
def getResistanceFactor = PostMicroClass.getResistanceFactor
}
@@ -1,84 +0,0 @@
package codechicken.microblock
import codechicken.lib.vec.Cuboid6
import codechicken.multipart.TFacePart
import codechicken.lib.vec.Vector3
import codechicken.lib.vec.Rotation
import codechicken.microblock.MicroMaterialRegistry.IMicroMaterial
import Rotation._
import Vector3._
import codechicken.lib.lighting.LightMatrix
object FacePlacement extends PlacementProperties
{
def microClass = FaceMicroClass
def placementGrid = FacePlacementGrid
def opposite(slot:Int, side:Int) = slot^1
override def expand(slot:Int, side:Int) = sneakOpposite(slot, side)
override def sneakOpposite(slot:Int, side:Int) = slot == (side^1)
}
object FaceMicroClass extends MicroblockClass
{
var aBounds:Array[Cuboid6] = new Array(256)
for(s <- 0 until 6)
{
val transform = sideRotations(s).at(center)
for(t <- 1 until 8)
{
val d = t/8D
aBounds(t<<4|s) = new Cuboid6(0, 0, 0, 1, d, 1).apply(transform)
}
}
def getName = "mcr_face"
def create(client:Boolean) =
if(client)
new FaceMicroblockClient
else
new FaceMicroblock
def create(size:Int, slot:Int, material:Int, client:Boolean) =
if(client)
new FaceMicroblockClient(size, slot, material)
else
new FaceMicroblock(size, slot, material)
def placementProperties = FacePlacement
def getResistanceFactor = 1
}
class FaceMicroblockClient(shape$:Byte = 0, material$:Int = 0) extends FaceMicroblock(shape$, material$) with CommonMicroblockClient
{
def this(size:Int, shape:Int, material:Int) = this((size<<4|shape).toByte, material)
override def render(pos:Vector3, pass:Int) {
if(pass < 0)
MicroblockRender.renderCuboid(pos, getIMaterial, pass, getBounds, 0)
else if(isTransparent)
MicroblockRender.renderCuboid(pos, getIMaterial, pass, renderBounds, renderMask)
else {
val mat = getIMaterial
MicroblockRender.renderCuboid(pos, mat, pass, renderBounds, renderMask | 1<<getSlot)
MicroblockRender.renderCuboid(pos, mat, pass, Cuboid6.full, ~(1<<getSlot))
}
}
}
class FaceMicroblock(shape$:Byte = 0, material$:Int = 0) extends CommonMicroblock(shape$, material$) with TFacePart
{
def this(size:Int, shape:Int, material:Int) = this((size<<4|shape).toByte, material)
def microClass = FaceMicroClass
def getBounds = FaceMicroClass.aBounds(shape)
override def solid(side:Int) = getIMaterial.isSolid
}
@@ -1,48 +0,0 @@
package codechicken.microblock
import codechicken.lib.vec.{Cuboid6, Vector3}
import net.minecraft.block.Block
import codechicken.lib.render.{ColourMultiplier, CCRenderState}
import net.minecraft.block.BlockGrass
import net.minecraft.init.Blocks
import codechicken.lib.render.uv.{UVTranslation, IconTransformation}
import codechicken.lib.render.BlockRenderer.BlockFace
class GrassMicroMaterial extends BlockMicroMaterial(Blocks.grass, 0)
{
var sideIconT:IconTransformation = _
override def loadIcons()
{
super.loadIcons()
sideIconT = new IconTransformation(BlockGrass.getIconSideOverlay)
}
override def renderMicroFace(pos:Vector3, pass:Int, bounds:Cuboid6) {
val face = CCRenderState.model.asInstanceOf[BlockFace]
if(pass != -1)
face.computeLightCoords()
if(face.side == 1)
MaterialRenderHelper.start(pos, pass, icont).blockColour(getColour(pass)).lighting().render()
else
MaterialRenderHelper.start(pos, pass, icont).lighting().render()
if(face.side > 1)
MaterialRenderHelper.start(pos, pass, new UVTranslation(0, bounds.max.y-1) ++ sideIconT)
.blockColour(getColour(pass)).lighting().render()
}
}
class TopMicroMaterial($block:Block, $meta:Int = 0) extends BlockMicroMaterial($block, $meta)
{
override def renderMicroFace(pos:Vector3, pass:Int, bounds:Cuboid6)
{
val face = CCRenderState.model.asInstanceOf[BlockFace]
if(face.side <= 1)
MaterialRenderHelper.start(pos, pass, icont).blockColour(getColour(pass)).lighting().render()
else
MaterialRenderHelper.start(pos, pass, new UVTranslation(0, bounds.max.y-1) ++ icont)
.blockColour(getColour(pass)).lighting().render()
}
}
@@ -1,253 +0,0 @@
package codechicken.microblock
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.util.MovingObjectPosition
import codechicken.multipart.TFacePart
import codechicken.lib.vec.{Translation, Cuboid6, Rotation, Vector3}
import codechicken.multipart.TNormalOcclusion
import codechicken.microblock.MicroMaterialRegistry.IMicroMaterial
import net.minecraft.client.renderer.RenderBlocks
import codechicken.lib.render.{BlockRenderer, RenderUtils, CCRenderState}
import scala.collection.JavaConversions._
import Rotation._
import Vector3._
import codechicken.lib.raytracer.IndexedCuboid6
import org.lwjgl.opengl.GL11
import codechicken.lib.render.uv.IconTransformation
import codechicken.lib.lighting.LightMatrix
object HollowPlacement extends PlacementProperties
{
object HollowPlacementGrid extends FaceEdgeGrid(3 / 8D)
def microClass = HollowMicroClass
def placementGrid = HollowPlacementGrid
def opposite(slot: Int, side: Int) = slot ^ 1
override def expand(slot: Int, side: Int) = sneakOpposite(slot, side)
override def sneakOpposite(slot: Int, side: Int) = slot == (side ^ 1)
}
object HollowMicroClass extends MicroblockClass
{
var pBoxes: Array[Seq[Cuboid6]] = new Array(256)
var occBounds: Array[Cuboid6] = new Array(256)
for (s <- 0 until 6) {
val transform = sideRotations(s).at(center)
for (t <- 1 until 8) {
val d = t / 8D
val w1 = 1 / 8D
val w2 = 3 / 16D
pBoxes(t << 4 | s) = Seq(
new Cuboid6(0, 0, 0, w1, d, 1),
new Cuboid6(1 - w1, 0, 0, 1, d, 1),
new Cuboid6(w1, 0, 0, 1 - w1, d, w1),
new Cuboid6(w1, 0, 1 - w1, 1 - w1, d, 1))
.map(_.apply(transform))
occBounds(t << 4 | s) = new Cuboid6(1 / 8D, 0, 1 / 8D, 7 / 8D, d, 7 / 8D).apply(transform)
}
}
def getName = "mcr_hllw"
def create(client: Boolean) =
if (client)
new HollowMicroblockClient
else
new HollowMicroblock
def create(size: Int, slot: Int, material: Int, client: Boolean) =
if (client)
new HollowMicroblockClient(size, slot, material)
else
new HollowMicroblock(size, slot, material)
def placementProperties = HollowPlacement
def getResistanceFactor = 1
}
class HollowMicroblockClient(shape$: Byte = 0, material$: Int = 0) extends HollowMicroblock(shape$, material$) with CommonMicroblockClient
{
renderMask |= 8 << 8
def this(size: Int, shape: Int, material: Int) = this((size << 4 | shape).toByte, material)
override def recalcBounds() {
super.recalcBounds()
renderMask = renderMask & 0xFF | getHollowSize << 8
}
override def drawBreaking(renderBlocks: RenderBlocks) {
CCRenderState.reset()
CCRenderState.setPipeline(new Translation(x, y, z), new IconTransformation(renderBlocks.overrideBlockTexture))
renderHollow(null, 0, getBounds, 0, false,
(pos: Vector3, mat: IMicroMaterial, pass: Int, c: Cuboid6, sideMask: Int) =>
BlockRenderer.renderCuboid(c, sideMask))
}
override def render(pos: Vector3, pass: Int) {
if (pass == -1)
renderHollow(pos, pass, getBounds, 0, false, MicroblockRender.renderCuboid)
else if (isTransparent)
renderHollow(pos, pass, renderBounds, renderMask, false, MicroblockRender.renderCuboid)
else {
renderHollow(pos, pass, renderBounds, renderMask | 1 << getSlot, false, MicroblockRender.renderCuboid)
renderHollow(pos, pass, Cuboid6.full, ~(1 << getSlot), true, MicroblockRender.renderCuboid)
}
}
def renderHollow(pos: Vector3, pass: Int, c: Cuboid6, sideMask: Int, face: Boolean,
f: (Vector3, IMicroMaterial, Int, Cuboid6, Int) => Unit) {
val mat = getIMaterial
val size = renderMask >> 8
val d1 = 0.5 - size / 32D
val d2 = 0.5 + size / 32D
val x1 = c.min.x
val x2 = c.max.x
val y1 = c.min.y
val y2 = c.max.y
val z1 = c.min.z
val z2 = c.max.z
var iMask = 0
getSlot match {
case 0 | 1 =>
if (face)
iMask = 0x3C
f(pos, mat, pass, new Cuboid6(d1, y1, d2, d2, y2, z2), 0x3B | iMask) //-z internal
f(pos, mat, pass, new Cuboid6(d1, y1, z1, d2, y2, d1), 0x37 | iMask) //+z internal
f(pos, mat, pass, new Cuboid6(d2, y1, d1, x2, y2, d2), sideMask & 0x23 | 0xC | iMask) //-x internal -y+y+x external
f(pos, mat, pass, new Cuboid6(x1, y1, d1, d1, y2, d2), sideMask & 0x13 | 0xC | iMask) //+x internal -y+y-x external
f(pos, mat, pass, new Cuboid6(x1, y1, d2, x2, y2, z2), sideMask & 0x3B | 4 | iMask) //-y+y+z-x+x external
f(pos, mat, pass, new Cuboid6(x1, y1, z1, x2, y2, d1), sideMask & 0x37 | 8 | iMask) //-y+y-z-x+x external
case 2 | 3 =>
if (face)
iMask = 0x33
f(pos, mat, pass, new Cuboid6(d2, d1, z1, x2, d2, z2), 0x2F | iMask) //-x internal
f(pos, mat, pass, new Cuboid6(x1, d1, z1, d1, d2, z2), 0x1F | iMask) //+x internal
f(pos, mat, pass, new Cuboid6(d1, d2, z1, d2, y2, z2), sideMask & 0xE | 0x30 | iMask) //-y internal -z+z+y external
f(pos, mat, pass, new Cuboid6(d1, y1, z1, d2, d1, z2), sideMask & 0xD | 0x30 | iMask) //+y internal -z+z-y external
f(pos, mat, pass, new Cuboid6(d2, y1, z1, x2, y2, z2), sideMask & 0x2F | 0x10 | iMask) //-z+z+x-y+y external
f(pos, mat, pass, new Cuboid6(x1, y1, z1, d1, y2, z2), sideMask & 0x1F | 0x20 | iMask) //-z+z-x-y+y external
case 4 | 5 =>
if (face)
iMask = 0xF
f(pos, mat, pass, new Cuboid6(x1, d2, d1, x2, y2, d2), 0x3E | iMask) //-y internal
f(pos, mat, pass, new Cuboid6(x1, y1, d1, x2, d1, d2), 0x3D | iMask) //+y internal
f(pos, mat, pass, new Cuboid6(x1, d1, d2, x2, d2, z2), sideMask & 0x38 | 3 | iMask) //-z internal -x+x+z external
f(pos, mat, pass, new Cuboid6(x1, d1, z1, x2, d2, d1), sideMask & 0x34 | 3 | iMask) //+z internal -x+x-z external
f(pos, mat, pass, new Cuboid6(x1, d2, z1, x2, y2, z2), sideMask & 0x3E | 1 | iMask) //-x+x+y-z+z external
f(pos, mat, pass, new Cuboid6(x1, y1, z1, x2, d1, z2), sideMask & 0x3D | 2 | iMask) //-x+x-y-z+z external
}
}
override def drawHighlight(hit: MovingObjectPosition, player: EntityPlayer, frame: Float): Boolean = {
val size = getHollowSize
val d1 = 0.5 - size / 32D
val d2 = 0.5 + size / 32D
val t = (shape >> 4) / 8D
import GL11._
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glDisable(GL_TEXTURE_2D)
glColor4f(0, 0, 0, 0.4F)
glLineWidth(2)
glDepthMask(false)
glPushMatrix()
RenderUtils.translateToWorldCoords(player, frame)
glTranslated(x, y, z)
sideRotations(shape & 0xF).at(center).glApply()
RenderUtils.drawCuboidOutline(new Cuboid6(0, 0, 0, 1, t, 1).expand(0.001))
RenderUtils.drawCuboidOutline(new Cuboid6(d1, 0, d1, d2, t, d2).expand(-0.001))
glPopMatrix()
glDepthMask(true)
glEnable(GL_TEXTURE_2D)
glDisable(GL_BLEND)
return true
}
}
class HollowMicroblock(shape$: Byte = 0, material$: Int = 0) extends CommonMicroblock(shape$, material$) with TFacePart with TNormalOcclusion
{
def this(size: Int, shape: Int, material: Int) = this((size << 4 | shape).toByte, material)
def microClass = HollowMicroClass
def getBounds: Cuboid6 = FaceMicroClass.aBounds(shape)
override def getPartialOcclusionBoxes = HollowMicroClass.pBoxes(shape)
def getHollowSize = tile match {
case null => 8
case _ => tile.partMap(6) match {
case part: ISidedHollowConnect => part.getHollowSize(getSlot)
case _ => 8
}
}
def getOcclusionBoxes = {
val size = getHollowSize
val c = HollowMicroClass.occBounds(shape)
val d1 = 0.5 - size / 32D
val d2 = 0.5 + size / 32D
val x1 = c.min.x
val x2 = c.max.x
val y1 = c.min.y
val y2 = c.max.y
val z1 = c.min.z
val z2 = c.max.z
getSlot match {
case 0 | 1 =>
Seq(new Cuboid6(d2, y1, d1, x2, y2, d2),
new Cuboid6(x1, y1, d1, d1, y2, d2),
new Cuboid6(x1, y1, d2, x2, y2, z2),
new Cuboid6(x1, y1, z1, x2, y2, d1))
case 2 | 3 =>
Seq(new Cuboid6(d1, d2, z1, d2, y2, z2),
new Cuboid6(d1, y1, z1, d2, d1, z2),
new Cuboid6(d2, y1, z1, x2, y2, z2),
new Cuboid6(x1, y1, z1, d1, y2, z2))
case 4 | 5 =>
Seq(new Cuboid6(x1, d1, d2, x2, d2, z2),
new Cuboid6(x1, d1, z1, x2, d2, d1),
new Cuboid6(x1, d2, z1, x2, y2, z2),
new Cuboid6(x1, y1, z1, x2, d1, z2))
}
}
override def getCollisionBoxes = {
val size = getHollowSize
val d1 = 0.5 - size / 32D
val d2 = 0.5 + size / 32D
val t = (shape >> 4) / 8D
val tr = sideRotations(shape & 0xF).at(center)
Seq(new Cuboid6(0, 0, 0, 1, t, d1),
new Cuboid6(0, 0, d2, 1, t, 1),
new Cuboid6(0, 0, d1, d1, t, d2),
new Cuboid6(d2, 0, d1, 1, t, d2))
.map(c => c.apply(tr))
}
override def getSubParts = getCollisionBoxes.map(c => new IndexedCuboid6(0, c))
override def allowCompleteOcclusion = true
override def solid(side: Int) = false
override def redstoneConductionMap = 0x10
}
@@ -1,22 +0,0 @@
package codechicken.microblock
import codechicken.lib.vec.Cuboid6
import net.minecraft.world.World
/**
* Wrapper class blocks/tiles/parts wanting to use the micro material system to render part of their models.
*/
trait IMicroMaterialRender {
/**
* May be null for inventory rendering.
*/
def world:World
def x:Int
def y:Int
def z:Int
/**
* Return the bounds of the part for texture mapping side decals like grass
*/
def getRenderBounds:Cuboid6
}
@@ -1,13 +0,0 @@
package codechicken.microblock
/**
* Implement this on center parts that connect through hollow covers to have the covers render their hollows to fit the connection size
*/
trait ISidedHollowConnect
{
/**
* @param side The side of the block on which the cover resides
* @return The size (width and height) of the connection in pixels. Must be be less than 12 and more than 0
*/
def getHollowSize(side:Int):Int
}
-169
View File
@@ -1,169 +0,0 @@
package codechicken.microblock
import net.minecraft.item.Item
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound
import ItemMicroPart._
import net.minecraftforge.client.IItemRenderer
import net.minecraftforge.client.IItemRenderer.ItemRenderType
import net.minecraftforge.client.IItemRenderer.ItemRendererHelper
import org.lwjgl.opengl.GL11
import codechicken.microblock.MicroMaterialRegistry.IMicroMaterial
import java.util.List
import net.minecraft.creativetab.CreativeTabs
import codechicken.microblock.MicroblockClassRegistry._
import codechicken.lib.render.CCRenderState
import net.minecraft.client.renderer.texture.IIconRegister
import codechicken.microblock.handler.MicroblockProxy
import net.minecraft.util.{StatCollector, MovingObjectPosition}
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.world.World
import codechicken.lib.raytracer.RayTracer
import codechicken.lib.vec.Vector3
import codechicken.lib.render.TextureUtils
import net.minecraft.util.MovingObjectPosition.MovingObjectType
class ItemMicroPart extends Item
{
setUnlocalizedName("microblock")
setHasSubtypes(true)
override def getItemStackDisplayName(stack:ItemStack):String =
{
val material = getMaterial(stack)
val mcrClass = MicroblockClassRegistry.getMicroClass(stack.getItemDamage)
val size = stack.getItemDamage&0xFF
if(material == null || mcrClass == null)
return "Unnamed"
return StatCollector.translateToLocalFormatted(mcrClass.getName+"."+size+".name", material.getLocalizedName)
}
override def getSubItems(item:Item, tab:CreativeTabs, list$:List[_])
{
val list = list$.asInstanceOf[List[ItemStack]]
for(classId <- 0 until classes.length)
{
val mcrClass = classes(classId)
if(mcrClass != null)
for(size <- Seq(1, 2, 4))
MicroMaterialRegistry.getIdMap.foreach(e => list.add(create(classId<<8|size, e._1)))
}
}
override def registerIcons(register:IIconRegister){}
override def onItemUse(item:ItemStack, player:EntityPlayer, world:World, x:Int, y:Int, z:Int, s:Int, hitX:Float, hitY:Float, hitZ:Float):Boolean =
{
val material = getMaterialID(item)
val mcrClass = MicroblockClassRegistry.getMicroClass(item.getItemDamage)
val size = item.getItemDamage&0xFF
if(material < 0 || mcrClass == null)
return false
val hit = RayTracer.retraceBlock(world, player, x, y, z)
if(hit != null && hit.typeOfHit == MovingObjectType.BLOCK)
{
val placement = MicroblockPlacement(player, hit, size, material, !player.capabilities.isCreativeMode, mcrClass.placementProperties)
if(placement == null)
return false
if(!world.isRemote)
{
placement.place(world, player, item)
if(!player.capabilities.isCreativeMode)
placement.consume(world, player, item)
val sound = MicroMaterialRegistry.getMaterial(material).getSound
if(sound != null)
world.playSoundEffect(placement.pos.x + 0.5D, placement.pos.y + 0.5D, placement.pos.z + 0.5D, sound.func_150496_b, (sound.getVolume + 1.0F) / 2.0F, sound.getPitch * 0.8F)
}
return true
}
return false
}
}
object ItemMicroPart
{
def checkTagCompound(stack:ItemStack)
{
if(!stack.hasTagCompound)
stack.setTagCompound(new NBTTagCompound())
}
def create(damage:Int, material:Int):ItemStack = create(damage, MicroMaterialRegistry.materialName(material))
def create(damage:Int, material:String):ItemStack = create(1, damage, material)
def create(amount:Int, damage:Int, material:String):ItemStack =
{
val stack = new ItemStack(MicroblockProxy.itemMicro, amount, damage)
checkTagCompound(stack)
stack.getTagCompound.setString("mat", material)
return stack
}
def getMaterial(stack:ItemStack):IMicroMaterial =
{
checkTagCompound(stack)
if(!stack.getTagCompound.hasKey("mat"))
return null
return MicroMaterialRegistry.getMaterial(stack.getTagCompound.getString("mat"))
}
def getMaterialID(stack:ItemStack):Int =
{
checkTagCompound(stack)
if(!stack.getTagCompound.hasKey("mat"))
return 0
return MicroMaterialRegistry.materialID(stack.getTagCompound.getString("mat"))
}
}
object ItemMicroPartRenderer extends IItemRenderer
{
def handleRenderType(item:ItemStack, t:ItemRenderType) = true
def shouldUseRenderHelper(t:ItemRenderType, item:ItemStack, helper:ItemRendererHelper) = true
def renderItem(t:ItemRenderType, item:ItemStack, data:Object*)
{
val material = getMaterial(item)
val mcrClass = MicroblockClassRegistry.getMicroClass(item.getItemDamage)
val size = item.getItemDamage&0xFF
if(material == null || mcrClass == null)
return
GL11.glPushMatrix()
if(t == ItemRenderType.ENTITY)
GL11.glScaled(0.5, 0.5, 0.5)
if(t == ItemRenderType.INVENTORY || t == ItemRenderType.ENTITY)
GL11.glTranslatef(-0.5F, -0.5F, -0.5F)
TextureUtils.bindAtlas(0)
CCRenderState.reset()
CCRenderState.useNormals = true
CCRenderState.pullLightmap()
CCRenderState.startDrawing()
val part = mcrClass.create(size, mcrClass.itemSlot, getMaterialID(item), true).asInstanceOf[MicroblockClient]
part.render(new Vector3(0.5, 0.5, 0.5).subtract(part.getBounds.center), -1)
CCRenderState.draw()
GL11.glPopMatrix()
}
def renderHighlight(player:EntityPlayer, stack:ItemStack, hit:MovingObjectPosition):Boolean =
{
val material = getMaterialID(stack)
val mcrClass = MicroblockClassRegistry.getMicroClass(stack.getItemDamage)
val size = stack.getItemDamage&0xFF
if(material < 0 || mcrClass == null)
return false
return MicroMaterialRegistry.renderHighlight(player, hit, mcrClass, size, material)
}
}
-98
View File
@@ -1,98 +0,0 @@
package codechicken.microblock
import net.minecraft.item.Item
import codechicken.lib.config.ConfigTag
import codechicken.microblock.handler.MicroblockProxy._
import net.minecraft.item.ItemStack
import net.minecraftforge.client.IItemRenderer
import codechicken.lib.render.CCModel
import net.minecraftforge.client.IItemRenderer.ItemRenderType
import net.minecraftforge.client.IItemRenderer.ItemRendererHelper
import codechicken.lib.vec.SwapYZ
import codechicken.lib.render.CCRenderState
import codechicken.lib.vec.TransformationList
import codechicken.lib.vec.Translation
import codechicken.lib.vec.Scale
import codechicken.lib.vec.Rotation
import org.lwjgl.opengl.GL11
import ItemRenderType._
import codechicken.lib.math.MathHelper._
import net.minecraft.util.ResourceLocation
import net.minecraft.client.renderer.texture.IIconRegister
import codechicken.lib.render.uv.UVTranslation
/**
* Interface for items that are 'saws'
*/
trait Saw extends Item
{
/**
* The maximum harvest level that some version of this saw is capable of cutting
*/
def getMaxCuttingStrength:Int = getCuttingStrength(new ItemStack(this))
/**
* The harvest level this saw is capable of cutting
*/
def getCuttingStrength(item:ItemStack):Int
}
class ItemSaw(sawTag:ConfigTag, val harvestLevel:Int) extends Item with Saw
{
{
val maxDamage = sawTag.getTag("durability").getIntValue(1<<harvestLevel+8)
if(maxDamage > 0)
setMaxDamage(maxDamage)
setNoRepair()
}
override def hasContainerItem = true
override def getContainerItem(stack:ItemStack) =
if(isDamageable)
new ItemStack(stack.getItem, 1, stack.getItemDamage+1)
else
stack
override def doesContainerItemLeaveCraftingGrid(stack:ItemStack) = false
def getCuttingStrength(item:ItemStack) = harvestLevel
override def registerIcons(register:IIconRegister){}
}
object ItemSawRenderer extends IItemRenderer
{
val models = CCModel.parseObjModels(new ResourceLocation("microblock", "models/saw.obj"), 7, new SwapYZ())
val handle = models.get("Handle")
val holder = models.get("BladeSupport")
val blade = models.get("Blade")
def handleRenderType(item:ItemStack, renderType:ItemRenderType) = true
def shouldUseRenderHelper(renderType:ItemRenderType, item:ItemStack, helper:ItemRendererHelper) = true
def renderItem(renderType:ItemRenderType, item:ItemStack, data:Object*)
{
val t = renderType match {
case INVENTORY => new TransformationList(new Scale(1.8), new Translation(0, 0, -0.6), new Rotation(-pi/4, 1, 0, 0), new Rotation(pi*3/4, 0, 1, 0))
case ENTITY => new TransformationList(new Scale(1), new Translation(0, 0, -0.25), new Rotation(-pi/4, 1, 0, 0))
case EQUIPPED_FIRST_PERSON => new TransformationList(new Scale(1.5), new Rotation(-pi/3, 1, 0, 0), new Rotation(pi*3/4, 0, 1, 0), new Translation(0.5, 0.5, 0.5))
case EQUIPPED => new TransformationList(new Scale(1.5), new Rotation(-pi/5, 1, 0, 0), new Rotation(-pi*3/4, 0, 1, 0), new Translation(0.75, 0.5, 0.75))
case _ => return
}
CCRenderState.reset()
CCRenderState.useNormals = true
CCRenderState.pullLightmap()
CCRenderState.changeTexture("microblock:textures/items/saw.png")
CCRenderState.startDrawing()
handle.render(t)
holder.render(t)
CCRenderState.draw()
GL11.glDisable(GL11.GL_CULL_FACE)
CCRenderState.startDrawing()
blade.render(t, new UVTranslation(0, (item.getItem.asInstanceOf[Saw].getCuttingStrength(item)-1)*4/64D))
CCRenderState.draw()
GL11.glEnable(GL11.GL_CULL_FACE)
}
}
@@ -1,236 +0,0 @@
package codechicken.microblock
import codechicken.multipart.IDWriter
import codechicken.lib.packet.PacketCustom
import scala.collection.mutable.HashMap
import codechicken.lib.data.MCDataOutput
import codechicken.lib.data.MCDataInput
import codechicken.multipart.MultiPartRegistry
import cpw.mods.fml.relauncher.SideOnly
import cpw.mods.fml.relauncher.Side
import codechicken.lib.vec.{Cuboid6, Vector3}
import net.minecraft.util.IIcon
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.item.{Item, ItemStack}
import scala.collection.mutable.ListBuffer
import net.minecraft.util.MovingObjectPosition
import net.minecraft.entity.Entity
import scala.collection.JavaConversions._
import java.util.{Iterator => JIterator}
import net.minecraft.block.Block.SoundType
object MicroMaterialRegistry
{
/**
* Interface for defining a micro material
*/
trait IMicroMaterial
{
/**
* The icon to be used for breaking particles on side
*/
@SideOnly(Side.CLIENT)
def getBreakingIcon(side: Int): IIcon
/**
* Callback to load icons from the underlying block/etc
*/
@SideOnly(Side.CLIENT)
def loadIcons() {}
/**
* This function must configure and use the CCRenderState pipeline to draw from the current vertex source.
* CCRenderState.lightMatrix will be set.
* @param pass The current render pass, -1 for inventory rendering
* @param bounds The cuboid bounds of the face being rendered
*/
@SideOnly(Side.CLIENT)
def renderMicroFace(pos: Vector3, pass: Int, bounds: Cuboid6)
/**
* Get the render pass for which this material renders in.
*/
@SideOnly(Side.CLIENT)
def canRenderInPass(pass: Int) = pass == 0
/**
* Return true if this material is not opaque (glass, ice).
*/
def isTransparent: Boolean
/**
* Return the light level emitted by this material (glowstone)
*/
def getLightValue: Int
/**
* Return the strength of this material
*/
def getStrength(player: EntityPlayer): Float
/**
* Return the localised name of this material (normally the block name)
*/
def getLocalizedName: String
/**
* Get the item that this material is cut from (full block -> slabs)
*/
def getItem: ItemStack
/**
* Get the strength of saw requried to cut this material
*/
def getCutterStrength: Int
/**
* Get the breaking/walking sound
*/
def getSound: SoundType
/**
* Return true if this material is solid and opaque (can run wires on etc)
*/
def isSolid = !isTransparent
/**
* Get the explosion resistance of this part to an explosion caused by entity
*/
def explosionResistance(entity: Entity): Float
}
/**
* Interface for overriding the default micro placement highlight handler to show the effect of placement on a certain block/part
*/
trait IMicroHighlightRenderer
{
/**
* Return true if a custom highlight was rendered and the default should be skipped
*/
def renderHighlight(player: EntityPlayer, hit: MovingObjectPosition, mcrClass: MicroblockClass, size: Int, material: Int): Boolean
}
private val typeMap = HashMap[String, IMicroMaterial]()
private val nameMap = HashMap[String, Int]()
private var idMap: Array[(String, IMicroMaterial)] = _
private val idWriter = new IDWriter
private val highlightRenderers = ListBuffer[IMicroHighlightRenderer]()
private var maxCuttingStrength: Int = _
/**
* Register a micro material with unique identifier name
*/
def registerMaterial(material: IMicroMaterial, name: String) {
if (MultiPartRegistry.loaded)
throw new IllegalStateException("You must register your materials in the init methods.")
if (typeMap.contains(name)) {
System.err.println("Material with id " + name + " is already registered.")
return
}
System.out.println("Registered micro material: " + name)
typeMap.put(name, material)
}
/**
* Replace a micro material with unique identifier name
*/
def replaceMaterial(material: IMicroMaterial, name: String) {
if (MultiPartRegistry.loaded)
throw new IllegalStateException("You must register your materials in the init methods.")
if (typeMap.remove(name).isEmpty)
System.err.println("Material with id " + name + " is was not registered.")
System.out.println("Replaced micro material: " + name)
typeMap.put(name, material)
}
/**
* Registers a highlight renderer
*/
def registerHighlightRenderer(handler: IMicroHighlightRenderer) {
highlightRenderers += handler
}
private[microblock] def setupIDMap() {
idMap = typeMap.toList.sortBy(_._1).toArray
idWriter.setMax(idMap.length)
nameMap.clear()
for (i <- 0 until idMap.length)
nameMap.put(idMap(i)._1, i)
}
private[microblock] def calcMaxCuttingStrength() {
val it = Item.itemRegistry.iterator.asInstanceOf[JIterator[Item]]
maxCuttingStrength = it.flatMap {
case saw: Saw => Some(saw.getMaxCuttingStrength)
case _ => None
}.max
}
private[microblock] def loadIcons() {
MicroMaterialRegistry.getIdMap.foreach(e => e._2.loadIcons())
}
def getMaxCuttingStrength = maxCuttingStrength
def writeIDMap(packet: PacketCustom) {
packet.writeInt(idMap.size)
idMap.foreach(e => packet.writeString(e._1))
}
def readIDMap(packet: PacketCustom): Seq[String] = {
val k = packet.readInt()
idWriter.setMax(k)
idMap = new Array(k)
nameMap.clear()
val missing = ListBuffer[String]()
for (i <- 0 until k) {
val s = packet.readString()
val v = typeMap.get(s)
if (v.isEmpty)
missing += s
else {
idMap(i) = (s, v.get)
nameMap.put(s, i)
}
}
return missing
}
def writeMaterialID(data: MCDataOutput, id: Int) {
idWriter.write(data, id)
}
def readMaterialID(data: MCDataInput) = idWriter.read(data)
def materialName(id: Int) = idMap(id)._1
def materialID(name: String) = nameMap.get(name) match {
case Some(v) => v
case None =>
System.err.println("Missing mapping for part with ID: " + name)
0
}
def getMaterial(name: String) = typeMap.getOrElse(name, null)
def getMaterial(id: Int) = idMap(id)._2
def getIdMap = idMap
def renderHighlight(player: EntityPlayer, hit: MovingObjectPosition, mcrClass: MicroblockClass, size: Int, material: Int): Boolean = {
val overridden = highlightRenderers.find(_.renderHighlight(player, hit, mcrClass, size, material))
if (overridden.isDefined)
return true
MicroblockRender.renderHighlight(player, hit, mcrClass, size, material)
return true
}
}
-228
View File
@@ -1,228 +0,0 @@
package codechicken.microblock
import net.minecraft.item.crafting.IRecipe
import net.minecraft.world.World
import net.minecraft.inventory.InventoryCrafting
import net.minecraft.item.ItemStack
import codechicken.microblock.handler.MicroblockProxy._
import net.minecraftforge.oredict.RecipeSorter
object MicroRecipe extends IRecipe
{
RecipeSorter.register("fmp:micro", getClass, RecipeSorter.Category.SHAPED, "after:forge:shapelessore")
def getRecipeOutput = new ItemStack(sawStone)
def getRecipeSize = 9
def matches(icraft:InventoryCrafting, world:World) = getCraftingResult(icraft) != null
def getCraftingResult(icraft:InventoryCrafting):ItemStack =
{
var res = getHollowResult(icraft)
if(res != null) return res
res = getGluingResult(icraft)
if(res != null) return res
res = getThinningResult(icraft)
if(res != null) return res
res = getSplittingResult(icraft)
if(res != null) return res
res = getHollowFillResult(icraft)
return res
}
def create(amount:Int, mcrClass:Int, size:Int, material:Int):ItemStack =
{
if(size == 8)
{
val item = MicroMaterialRegistry.getMaterial(material).getItem.copy
item.stackSize = amount
return item
}
return ItemMicroPart.create(amount, mcrClass<<8|size, MicroMaterialRegistry.materialName(material))
}
def microMaterial(item:ItemStack) =
if(item.getItem == itemMicro)
ItemMicroPart.getMaterialID(item)
else
findMaterial(item)
def microClass(item:ItemStack) =
if(item.getItem == itemMicro)
item.getItemDamage >> 8
else
0
def microSize(item:ItemStack) =
if(item.getItem == itemMicro)
item.getItemDamage & 0xFF
else
8
def getHollowResult(icraft:InventoryCrafting):ItemStack =
{
if(icraft.getStackInRowAndColumn(1, 1) != null) return null
val first = icraft.getStackInRowAndColumn(0, 0)
if(first == null || first.getItem != itemMicro || microClass(first) != 0) return null
val size = microSize(first)
val material = microMaterial(first)
for(i <- 1 to 8 if i != 4)
{
val item = icraft.getStackInSlot(i)
if(item == null || item.getItem != itemMicro ||
microMaterial(item) != material || item.getItemDamage != first.getItemDamage)
return null
}
return create(8, 1, size, material)
}
def getGluingResult(icraft:InventoryCrafting):ItemStack =
{
var size = 0
var count = 0
var smallest = 0
var mcrClass = 0
var material = 0
for(i <- 0 until 9)
{
val item = icraft.getStackInSlot(i)
if(item != null)
{
if(item.getItem != itemMicro) return null
if(count == 0)
{
size = microSize(item)
mcrClass = microClass(item)
material = microMaterial(item)
count = 1
smallest = size
}
else if(microClass(item) != mcrClass || microMaterial(item) != material) return null
else if(mcrClass >= 2 && microSize(item) != smallest) return null
else
{
smallest= Math.min(smallest, microSize(item))
count+=1
size+=microSize(item)
}
}
}
if(count <= 1) return null
val matName = MicroMaterialRegistry.materialName(material)
mcrClass match {
case 3 => count match {
case 2 => create(1, 0, smallest, material)
case _ => null
}
case 2 => count match {
case 2 => create(1, 3, smallest, material)
case 4 => create(1, 0, smallest, material)
case _ => null
}
case 1|0 =>
val base = Seq(1, 2, 4).find(s => (s&size) != 0)
if(base.isEmpty)
create(size/8, 0, 8, material)
else if(base.get <= smallest)
null
else
create(size/base.get, mcrClass, base.get, material)
case _ => null
}
}
def getSaw(icraft:InventoryCrafting):(Saw, Int, Int) =
{
for(r <- 0 until 3)
for(c <- 0 until 3)
{
val item = icraft.getStackInRowAndColumn(c, r)
if(item != null && item.getItem.isInstanceOf[Saw])
return (item.getItem.asInstanceOf[Saw], r, c)
}
return (null, 0, 0)
}
def canCut(saw:Saw, sawItem:ItemStack, material:Int):Boolean = {
val sawStrength = saw.getCuttingStrength(sawItem)
val matStrength = MicroMaterialRegistry.getMaterial(material).getCutterStrength
return sawStrength >= matStrength || sawStrength == MicroMaterialRegistry.getMaxCuttingStrength
}
def getThinningResult(icraft:InventoryCrafting):ItemStack =
{
val (saw, row, col) = getSaw(icraft)
if(saw == null)
return null
val item = icraft.getStackInRowAndColumn(col, row+1)
if(item == null)
return null
val size = microSize(item)
val material = microMaterial(item)
val mcrClass = microClass(item)
if(size == 1 || material < 0 || !canCut(saw, icraft.getStackInRowAndColumn(col, row), material))
return null
for(r <- 0 until 3)
for(c <- 0 until 3)
if((c != col || r != row && r != row+1) &&
icraft.getStackInRowAndColumn(c, r) != null)
return null
return create(2, mcrClass, size/2, material)
}
def findMaterial(item:ItemStack):Int =
MicroMaterialRegistry.getIdMap.find{m => val mitem = m._2.getItem
item.getItem == mitem.getItem &&
item.getItemDamage == mitem.getItemDamage &&
ItemStack.areItemStackTagsEqual(item, mitem)} match {
case None => -1
case Some((name, m)) => MicroMaterialRegistry.materialID(name)
}
val splitMap = Map(0 -> 3, 1 -> 3, 3 -> 2)
def getSplittingResult(icraft:InventoryCrafting):ItemStack =
{
val (saw, row, col) = getSaw(icraft)
if(saw == null) return null
val item = icraft.getStackInRowAndColumn(col+1, row)
if(item == null || item.getItem != itemMicro) return null
val mcrClass = microClass(item)
val material = microMaterial(item)
if(!canCut(saw, icraft.getStackInRowAndColumn(col, row), material)) return null
val split = splitMap.get(mcrClass)
if(split.isEmpty)return null
for(r <- 0 until 3)
for(c <- 0 until 3)
if((r != row || c != col && c != col+1) &&
icraft.getStackInRowAndColumn(c, r) != null)
return null
return create(2, split.get, microSize(item), material)
}
def getHollowFillResult(icraft:InventoryCrafting):ItemStack =
{
var cover:ItemStack = null
for(i <- 0 until 9)
{
val item = icraft.getStackInSlot(i)
if(item != null)
{
if(item.getItem != itemMicro || cover != null || microClass(item) != 1) return null
else cover = item
}
}
if(cover == null) return null
return create(1, 0, microSize(cover), microMaterial(cover))
}
}
@@ -1,20 +0,0 @@
package codechicken.microblock
trait MicroblockClass
{
var classID:Int = _
def itemSlot = 3
def getName:String
def create(client:Boolean):CommonMicroblock
def create(size:Int, slot:Int, material:Int, client:Boolean):CommonMicroblock
def placementProperties:PlacementProperties
def register(id:Int) = MicroblockClassRegistry.registerMicroClass(this, id)
def getResistanceFactor:Float
}
@@ -1,20 +0,0 @@
package codechicken.microblock
import codechicken.multipart.MultiPartRegistry
object MicroblockClassRegistry
{
val classes = new Array[MicroblockClass](256)
def getMicroClass(modelId:Int) = classes(modelId>>8)
def registerMicroClass(mcrClass:MicroblockClass, id:Int)
{
if(classes(id) != null)
throw new IllegalArgumentException("Microblock class id "+id+" is already taken by "+classes(id).getName+" when adding "+mcrClass.getName)
classes(id) = mcrClass
mcrClass.classID = id
MultiPartRegistry.registerParts((_, c:Boolean) => mcrClass.create(c), mcrClass.getName)
}
}
@@ -1,175 +0,0 @@
package codechicken.microblock
import net.minecraft.world.World
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.util.MovingObjectPosition
import codechicken.lib.vec.Vector3
import codechicken.lib.vec.BlockCoord
import codechicken.lib.vec.Rotation
import codechicken.multipart.ControlKeyModifer._
import net.minecraft.item.ItemStack
import codechicken.multipart.TileMultipart
import codechicken.lib.raytracer.ExtendedMOP
abstract class ExecutablePlacement(val pos:BlockCoord, val part:Microblock)
{
def place(world:World, player:EntityPlayer, item:ItemStack)
def consume(world:World, player:EntityPlayer, item:ItemStack)
}
class AdditionPlacement($pos:BlockCoord, $part:Microblock) extends ExecutablePlacement($pos, $part)
{
def place(world:World, player:EntityPlayer, item:ItemStack)
{
TileMultipart.addPart(world, pos, part)
}
def consume(world:World, player:EntityPlayer, item:ItemStack)
{
item.stackSize-=1
}
}
class ExpandingPlacement($pos:BlockCoord, $part:Microblock, opart:Microblock) extends ExecutablePlacement($pos, $part)
{
def place(world:World, player:EntityPlayer, item:ItemStack)
{
opart.shape = part.shape
opart.tile.notifyPartChange(opart)
opart.sendDescUpdate()
}
def consume(world:World, player:EntityPlayer, item:ItemStack)
{
item.stackSize-=1
}
}
abstract class PlacementProperties
{
def opposite(slot:Int, side:Int):Int
def sneakOpposite(slot:Int, side:Int) = true
def expand(slot:Int, side:Int) = true
def microClass:MicroblockClass
def placementGrid:PlacementGrid
def customPlacement(pmt:MicroblockPlacement):ExecutablePlacement = null
}
object MicroblockPlacement
{
def apply(player:EntityPlayer, hit:MovingObjectPosition, size:Int, material:Int, checkMaterial:Boolean, pp:PlacementProperties):ExecutablePlacement =
new MicroblockPlacement(player, hit, size, material, checkMaterial, pp)()
}
class MicroblockPlacement(val player:EntityPlayer, val hit:MovingObjectPosition, val size:Int, val material:Int, val checkMaterial:Boolean, val pp:PlacementProperties)
{
val world = player.worldObj
val mcrClass = pp.microClass
val pos = new BlockCoord(hit.blockX, hit.blockY, hit.blockZ)
val vhit = new Vector3(hit.hitVec).add(-pos.x, -pos.y, -pos.z)
val gtile = TileMultipart.getOrConvertTile2(world, pos)
val htile = gtile._1
val slot = pp.placementGrid.getHitSlot(vhit, hit.sideHit)
val oslot = pp.opposite(slot, hit.sideHit)
val d = getHitDepth(vhit, hit.sideHit)
val useOppMod = pp.sneakOpposite(slot, hit.sideHit)
val oppMod = player.isControlDown
val internal = d < 1 && htile != null
val doExpand = internal && !gtile._2 && !player.isSneaking && !(oppMod && useOppMod) && pp.expand(slot, hit.sideHit)
val side = hit.sideHit
def apply():ExecutablePlacement =
{
val customPlacement = pp.customPlacement(this)
if(customPlacement != null)
return customPlacement
if(slot < 0)
return null
if(doExpand)
{
val hpart = htile.partList(ExtendedMOP.getData[(Int, _)](hit)._1)
if(hpart.getType == mcrClass.getName)
{
val mpart = hpart.asInstanceOf[CommonMicroblock]
if(mpart.material == material && mpart.getSize + size < 8)
return expand(mpart)
}
}
if(internal)
{
if(!useOppMod)
return internalPlacement(htile, slot)
if((1-d)*8 < size)
{
if(htile.canAddPart(create(1, oslot, material)))
return externalPlacement(slot)
}
if(d < 0.5)
{
val ret = internalPlacement(htile, slot)
if(ret != null && !oppMod)
return ret
if(ret != null && oppMod || ret == null && !oppMod)
return internalPlacement(htile, oslot)
//sneaking
if(internalPlacement(htile, oslot) != null)
return externalPlacement(slot)
return null
}
val ret = internalPlacement(htile, oslot)
if(!oppMod)
return ret
if(ret != null)
return externalPlacement(slot)
return null
}
val ret = externalPlacement(slot)
if(ret != null && oppMod && useOppMod)
return externalPlacement(oslot)
return ret
}
def expand(mpart:CommonMicroblock):ExecutablePlacement = expand(mpart, create(mpart.getSize+size, mpart.getSlot, mpart.material))
def expand(mpart:Microblock, npart:Microblock):ExecutablePlacement =
{
val pos = new BlockCoord(mpart.tile)
if(TileMultipart.checkNoEntityCollision(world, pos, npart) && mpart.tile.canReplacePart(mpart, npart))
return new ExpandingPlacement(pos, npart, mpart)
return null
}
def internalPlacement(htile:TileMultipart, slot:Int):ExecutablePlacement = internalPlacement(htile, create(size, slot, material))
def internalPlacement(htile:TileMultipart, npart:Microblock):ExecutablePlacement =
{
val pos = new BlockCoord(htile)
if(TileMultipart.checkNoEntityCollision(world, pos, npart) && htile.canAddPart(npart))
return new AdditionPlacement(pos, npart)
return null
}
def externalPlacement(slot:Int):ExecutablePlacement = externalPlacement(create(size, slot, material))
def externalPlacement(npart:Microblock):ExecutablePlacement =
{
val pos = this.pos.copy().offset(side)
if(TileMultipart.canPlacePart(world, pos, npart))
return new AdditionPlacement(pos, npart)
return null
}
def getHitDepth(vhit:Vector3, side:Int):Double =
vhit.copy.scalarProject(Rotation.axes(side)) + (side%2^1)
def create(size:Int, slot:Int, material:Int) = mcrClass.create(size, slot, material, world.isRemote)
}
-164
View File
@@ -1,164 +0,0 @@
package codechicken.microblock
import codechicken.lib.vec.Vector3
import org.lwjgl.opengl.GL11
import codechicken.lib.vec.BlockCoord
import codechicken.lib.vec.Rotation
import Rotation._
trait PlacementGrid
{
def getHitSlot(vhit:Vector3, side:Int):Int
def render(hit:Vector3, side:Int)
def glTransformFace(hit:Vector3, side:Int)
{
val pos = new BlockCoord(hit)
GL11.glPushMatrix()
GL11.glTranslated(pos.x+0.5, pos.y+0.5, pos.z+0.5)
sideRotations(side).glApply()
val rhit = new Vector3(pos.x+0.5, pos.y+0.5, pos.z+0.5).subtract(hit).apply(sideRotations(side^1).inverse)
GL11.glTranslated(0, rhit.y-0.002, 0)
}
}
class FaceEdgeGrid(size:Double) extends PlacementGrid
{
def render(hit:Vector3, side:Int)
{
glTransformFace(hit, side)
GL11.glLineWidth(2)
GL11.glColor4f(0, 0, 0, 1)
GL11.glBegin(GL11.GL_LINES)
GL11.glVertex3d(-0.5, 0,-0.5)
GL11.glVertex3d(-0.5, 0, 0.5)
GL11.glVertex3d(-0.5, 0, 0.5)
GL11.glVertex3d( 0.5, 0, 0.5)
GL11.glVertex3d( 0.5, 0, 0.5)
GL11.glVertex3d( 0.5, 0,-0.5)
GL11.glVertex3d( 0.5, 0,-0.5)
GL11.glVertex3d(-0.5, 0,-0.5)
GL11.glVertex3d(0.5, 0, 0.5)
GL11.glVertex3d(size, 0, size)
GL11.glVertex3d(-0.5, 0, 0.5)
GL11.glVertex3d(-size, 0, size)
GL11.glVertex3d(0.5, 0, -0.5)
GL11.glVertex3d(size, 0, -size)
GL11.glVertex3d(-0.5, 0, -0.5)
GL11.glVertex3d(-size, 0, -size)
GL11.glVertex3d(-size, 0,-size)
GL11.glVertex3d(-size, 0, size)
GL11.glVertex3d(-size, 0, size)
GL11.glVertex3d( size, 0, size)
GL11.glVertex3d( size, 0, size)
GL11.glVertex3d( size, 0,-size)
GL11.glVertex3d( size, 0,-size)
GL11.glVertex3d(-size, 0,-size)
GL11.glEnd()
GL11.glPopMatrix()
}
def getHitSlot(vhit:Vector3, side:Int):Int =
{
val s1 = (side+2)%6
val s2 = (side+4)%6
val u = vhit.copy().add(-0.5, -0.5, -0.5).scalarProject(Rotation.axes(s1))
val v = vhit.copy().add(-0.5, -0.5, -0.5).scalarProject(Rotation.axes(s2))
if(Math.abs(u) < size && Math.abs(v) < size)
return side^1
if(Math.abs(u) > Math.abs(v))
return if(u > 0) s1 else s1^1
else
return if(v > 0) s2 else s2^1
}
}
object FacePlacementGrid extends FaceEdgeGrid(1/4D)
object CornerPlacementGrid extends PlacementGrid
{
def render(hit:Vector3, side:Int)
{
glTransformFace(hit, side)
GL11.glLineWidth(2)
GL11.glColor4f(0, 0, 0, 1)
GL11.glBegin(GL11.GL_LINES)
GL11.glVertex3d(-0.5, 0,-0.5)
GL11.glVertex3d(-0.5, 0, 0.5)
GL11.glVertex3d(-0.5, 0, 0.5)
GL11.glVertex3d( 0.5, 0, 0.5)
GL11.glVertex3d( 0.5, 0, 0.5)
GL11.glVertex3d( 0.5, 0,-0.5)
GL11.glVertex3d( 0.5, 0,-0.5)
GL11.glVertex3d(-0.5, 0,-0.5)
GL11.glVertex3d(0, 0,-0.5)
GL11.glVertex3d(0, 0, 0.5)
GL11.glVertex3d(-0.5, 0, 0)
GL11.glVertex3d( 0.5, 0, 0)
GL11.glEnd()
GL11.glPopMatrix()
}
def getHitSlot(vhit:Vector3, side:Int):Int =
{
val s1 = ((side&6)+3)%6
val s2 = ((side&6)+5)%6
val u = vhit.copy().add(-0.5, -0.5, -0.5).scalarProject(Rotation.axes(s1))
val v = vhit.copy().add(-0.5, -0.5, -0.5).scalarProject(Rotation.axes(s2))
val bu = if(u >= 0) 1 else 0
val bv = if(v >= 0) 1 else 0
val bw = (side&1)^1
return 7+(
bw<<(side>>1)|
bu<<(s1>>1)|
bv<<(s2>>1))
}
}
object EdgePlacementGrid extends FaceEdgeGrid(1/4D)
{
override def getHitSlot(vhit:Vector3, side:Int):Int =
{
val s1 = (side+2)%6
val s2 = (side+4)%6
val u = vhit.copy().add(-0.5, -0.5, -0.5).scalarProject(Rotation.axes(s1))
val v = vhit.copy().add(-0.5, -0.5, -0.5).scalarProject(Rotation.axes(s2))
if(Math.abs(u) < 4/16D && Math.abs(v) < 4/16D)
return -1
var b = side&1
if(Math.abs(u) > Math.abs(v))
{
if(u < 0) b^=1
return 15+((s2&6)<<1 | b<<1 | side&1^1)
}
else
{
if(v < 0) b^=1
return 15+((s1&6)<<1 | (side&1^1)<<1 | b)
}
}
}
@@ -1,261 +0,0 @@
package codechicken.microblock
import codechicken.multipart.TMultiPart
import codechicken.lib.vec.Cuboid6
import codechicken.multipart.PartMap._
import codechicken.lib.data.MCDataInput
/**
* Suite of classes for performing microblock style occlusion and shrink rendering. Use at your own peril.
*/
object MicroOcclusion
{
def shrink(renderBounds:Cuboid6, b:Cuboid6, side:Int) = side match
{
case -1 =>
case 0 => if(renderBounds.min.y < b.max.y)renderBounds.min.y = b.max.y
case 1 => if(renderBounds.max.y > b.min.y)renderBounds.max.y = b.min.y
case 2 => if(renderBounds.min.z < b.max.z)renderBounds.min.z = b.max.z
case 3 => if(renderBounds.max.z > b.min.z)renderBounds.max.z = b.min.z
case 4 => if(renderBounds.min.x < b.max.x)renderBounds.min.x = b.max.x
case 5 => if(renderBounds.max.x > b.min.x)renderBounds.max.x = b.min.x
}
def shrinkFrom(p:JMicroShrinkRender, other:JMicroShrinkRender, renderBounds:Cuboid6):Int =
{
if(shrinkTest(p, other))
{
shrink(renderBounds, other.getBounds, shrinkSide(p.getSlot, other.getSlot))
}
else if(other.getSlot < 6 && !other.isTransparent)//other gets full face, we didn't shrink, flag rendermask
{
if(other.getSlot match {
case 0 => renderBounds.min.y <= 0
case 1 => renderBounds.max.y >= 1
case 2 => renderBounds.min.z <= 0
case 3 => renderBounds.max.z >= 1
case 4 => renderBounds.min.x <= 0
case 5 => renderBounds.max.x >= 1
})
return 1<<other.getSlot
}
return 0
}
def shrink(p:JMicroShrinkRender, renderBounds:Cuboid6, m:Int):Int =
{
var renderMask = 0
val part = p.asInstanceOf[TMultiPart]
val tile = part.tile
for(i <- 0 until m)
if(i != p.getSlot)
tile.partMap(i) match {
case other:JMicroShrinkRender =>
renderMask |= shrinkFrom(p, other, renderBounds)
case _ =>
}
return renderMask
}
def shrinkSide(s1:Int, s2:Int):Int = {
if(s2 < 6)//other is a cover
return s2
if(s1 < 15)//both corners
{
val c1 = s1-7
val c2 = s2-7
return c1^c2 match//different axis bits will become 1, same 0
{
case 1 => c2&1
case 2 => 2|(c2&2)>>1
case 4 => 4|(c2&4)>>2
case _ => -1
}
}
if(s2 < 15)//edge, other corner
{
val e1 = s1-15
val c2 = s2-7
val ebits = unpackEdgeBits(e1)
if((c2&edgeAxisMask(e1)) != ebits)
return -1
return (e1&0xC)>>1|(c2&(~ebits))>>(e1>>2)
}
//both edges
{
val e1 = s1-15
val e2 = s2-15
val e1bits = unpackEdgeBits(e1)
val e2bits = unpackEdgeBits(e2)
if((e1&0xC) == (e2&0xC))//same axis
{
return e1bits^e2bits match
{
case 1 => if((e2bits&1) == 0) 0 else 1
case 2 => if((e2bits&2) == 0) 2 else 3
case 4 => if((e2bits&4) == 0) 4 else 5
case _ => -1
}
}
else
{
val mask = edgeAxisMask(e1)&edgeAxisMask(e2)
if((e1bits&mask) != (e2bits&mask))
return -1
return e1>>2 match
{
case 0 => if((e2bits&1) == 0) 0 else 1
case 1 => if((e2bits&2) == 0) 2 else 3
case 2 => if((e2bits&4) == 0) 4 else 5
}
}
}
throw new IllegalArgumentException("Switch Falloff")
}
def recalcBounds(p:JMicroShrinkRender, renderBounds:Cuboid6) =
{
if(p.getSlot < 6)
shrink(p, renderBounds, 6)
else if(p.getSlot < 15)
shrink(p, renderBounds, 15)
else
shrink(p, renderBounds, 27)
}
def shapePriority(slot:Int):Int =
if(slot < 6)
return 2
else if(slot < 15)
return 1
else
return 0
def shrinkTest(a:JMicroShrinkRender, b:JMicroShrinkRender):Boolean =
{
if(a.getPriorityClass != b.getPriorityClass) return a.getPriorityClass < b.getPriorityClass
val shape1 = shapePriority(a.getSlot)
val shape2 = shapePriority(b.getSlot)
if(shape1 != shape2) return shape1 < shape2
if(a.getSlot < 6)//transparency takes precedence for covers
{
if(a.isTransparent != b.isTransparent) return a.isTransparent
if(a.getSize != b.getSize) return a.getSize < b.getSize
}
else
{
if(a.getSize != b.getSize) return a.getSize < b.getSize
if(a.isTransparent != b.isTransparent) return a.isTransparent
}
return a.getSlot < b.getSlot
}
}
trait JMicroShrinkRender
{
def getPriorityClass:Int
def getSlot:Int
def getSize:Int
def isTransparent:Boolean
def getBounds:Cuboid6
}
trait TMicroOcclusionClient extends TMicroOcclusion with JMicroShrinkRender
{
var renderBounds:Cuboid6 = _
var renderMask:Int = _
override def onPartChanged(part:TMultiPart)
{
super.onPartChanged(part)
recalcBounds()
}
override def onAdded()
{
super.onAdded()
recalcBounds()
}
override def read(packet:MCDataInput)
{
super.read(packet)
recalcBounds()
}
def recalcBounds()
{
renderBounds = getBounds.copy
renderMask = MicroOcclusion.recalcBounds(this, renderBounds)
}
override def getPriorityClass = 0
}
trait TMicroOcclusion extends TMultiPart
{
import MicroOcclusion._
def getSlot:Int
def getSize:Int
def getMaterial:Int
def getBounds:Cuboid6
override def occlusionTest(npart:TMultiPart):Boolean =
{
if(!super.occlusionTest(npart))
return false
if(!npart.isInstanceOf[TMicroOcclusion])
return true
val mpart = npart.asInstanceOf[TMicroOcclusion]
val shape1 = shapePriority(getSlot)
val shape2 = shapePriority(mpart.getSlot)
if(mpart.getSize + getSize > 8)//intersecting if opposite
{
if(shape1 == 2 && shape2 == 2)
if(mpart.getSlot == (getSlot^1))
return false
if(mpart.getMaterial != getMaterial)
{
if(shape1 == 1 && shape2 == 1)
{
val axisMask = (getSlot-7)^(mpart.getSlot-7)
if(axisMask == 3 || axisMask == 5 || axisMask == 6)
return false
}
if(shape1 == 0 && shape2 == 1)
if(!edgeCornerOcclusionTest(this, mpart))
return false
if(shape1 == 1 && shape2 == 0)
if(!edgeCornerOcclusionTest(mpart, this))
return false
if(shape1 == 0 && shape2 == 0)
{
val e1 = getSlot-15
val e2 = mpart.getSlot-15
if((e1&0xC) == (e2&0xC) && ((e1&3)^(e2&3)) == 3)
return false
}
}
}
return true
}
def edgeCornerOcclusionTest(edge:TMicroOcclusion, corner:TMicroOcclusion):Boolean =
{
((corner.getSlot-7)&edgeAxisMask(edge.getSlot-15)) == unpackEdgeBits(edge.getSlot-15)
}
}
@@ -1,40 +0,0 @@
package codechicken.microblock.handler
import cpw.mods.fml.common.eventhandler.SubscribeEvent
import cpw.mods.fml.relauncher.SideOnly
import cpw.mods.fml.relauncher.Side
import net.minecraftforge.client.event.TextureStitchEvent
import codechicken.microblock.MicroMaterialRegistry
import net.minecraftforge.client.event.DrawBlockHighlightEvent
import org.lwjgl.opengl.GL11
import codechicken.lib.render.RenderUtils
import codechicken.microblock.ItemMicroPartRenderer
import cpw.mods.fml.common.Loader
import cpw.mods.fml.common.LoaderState
import net.minecraft.util.MovingObjectPosition.MovingObjectType
object MicroblockEventHandler
{
@SubscribeEvent
@SideOnly(Side.CLIENT)
def postTextureStitch(event:TextureStitchEvent.Post)
{
if(event.map.getTextureType == 0 && Loader.instance.hasReachedState(LoaderState.POSTINITIALIZATION))
MicroMaterialRegistry.loadIcons()
}
@SubscribeEvent
@SideOnly(Side.CLIENT)
def drawBlockHighlight(event:DrawBlockHighlightEvent)
{
if(event.currentItem != null && event.currentItem.getItem == MicroblockProxy.itemMicro &&
event.target != null && event.target.typeOfHit == MovingObjectType.BLOCK)
{
GL11.glPushMatrix()
RenderUtils.translateToWorldCoords(event.player, event.partialTicks)
if(ItemMicroPartRenderer.renderHighlight(event.player, event.currentItem, event.target))
event.setCanceled(true)
GL11.glPopMatrix()
}
}
}
@@ -1,52 +0,0 @@
package codechicken.microblock.handler
import cpw.mods.fml.common.Mod
import cpw.mods.fml.common.event.FMLInitializationEvent
import cpw.mods.fml.common.event.FMLPreInitializationEvent
import cpw.mods.fml.common.Mod.EventHandler
import cpw.mods.fml.common.event.FMLServerAboutToStartEvent
import codechicken.microblock.MicroMaterialRegistry
import codechicken.microblock.DefaultContent
import cpw.mods.fml.common.event.FMLPostInitializationEvent
import codechicken.microblock.ConfigContent
import cpw.mods.fml.common.event.FMLInterModComms.IMCEvent
import scala.collection.JavaConversions._
@Mod(modid = "ForgeMicroblock", acceptedMinecraftVersions = "[1.7.2]",
dependencies="required-after:ForgeMultipart;after:*", modLanguage="scala")
object MicroblockMod
{
@EventHandler
def preInit(event:FMLPreInitializationEvent)
{
MicroblockProxy.preInit()
DefaultContent.load()
ConfigContent.parse(event.getModConfigurationDirectory)
}
@EventHandler
def init(event:FMLInitializationEvent)
{
MicroblockProxy.init()
ConfigContent.load()
}
@EventHandler
def postInit(event:FMLPostInitializationEvent)
{
MicroMaterialRegistry.setupIDMap()
MicroblockProxy.postInit()
}
@EventHandler
def beforeServerStart(event:FMLServerAboutToStartEvent)
{
MicroMaterialRegistry.setupIDMap()
}
@EventHandler
def handleIMC(event:IMCEvent)
{
ConfigContent.handleIMC(event.getMessages)
}
}
@@ -1,42 +0,0 @@
package codechicken.microblock.handler
import codechicken.lib.packet.PacketCustom.{IHandshakeHandler, IClientPacketHandler, IServerPacketHandler}
import codechicken.lib.packet.PacketCustom
import net.minecraft.client.Minecraft
import codechicken.microblock.MicroMaterialRegistry
import net.minecraft.entity.player.EntityPlayerMP
import net.minecraft.network.play.{INetHandlerPlayServer, INetHandlerPlayClient}
import net.minecraft.network.play.server.S40PacketDisconnect
import net.minecraft.util.ChatComponentTranslation
import net.minecraft.network.NetHandlerPlayServer
class MicroblockPH
{
val registryChannel = "ForgeMicroblock" //Must use the 250 system for ID registry as the NetworkMod idMap hasn't been properly initialized from the server yet.
}
object MicroblockCPH extends MicroblockPH with IClientPacketHandler
{
def handlePacket(packet: PacketCustom, mc: Minecraft, netHandler:INetHandlerPlayClient) {
packet.getType match {
case 1 => handleMaterialRegistration(packet, netHandler)
}
}
def handleMaterialRegistration(packet: PacketCustom, netHandler: INetHandlerPlayClient) {
val missing = MicroMaterialRegistry.readIDMap(packet)
if (!missing.isEmpty)
netHandler.handleDisconnect(new S40PacketDisconnect(new ChatComponentTranslation("microblock.missing", missing.mkString(", "))))
}
}
object MicroblockSPH extends MicroblockPH with IServerPacketHandler with IHandshakeHandler
{
def handlePacket(packet: PacketCustom, sender: EntityPlayerMP, netHandler:INetHandlerPlayServer) {}
def handshakeRecieved(netHandler: NetHandlerPlayServer) {
val packet = new PacketCustom(registryChannel, 1)
MicroMaterialRegistry.writeIDMap(packet)
netHandler.sendPacket(packet.toPacket)
}
}
@@ -1,115 +0,0 @@
package codechicken.microblock.handler
import net.minecraftforge.common.MinecraftForge
import codechicken.multipart.handler.MultipartProxy._
import codechicken.microblock._
import net.minecraftforge.client.MinecraftForgeClient
import cpw.mods.fml.relauncher.SideOnly
import cpw.mods.fml.relauncher.Side
import net.minecraft.item.crafting.CraftingManager
import net.minecraft.item.Item
import net.minecraft.item.crafting.IRecipe
import java.util.{List => JList}
import codechicken.lib.config.ConfigFile
import net.minecraftforge.oredict.OreDictionary
import net.minecraft.item.ItemStack
import net.minecraftforge.oredict.ShapedOreRecipe
import net.minecraft.block.Block
import codechicken.lib.packet.PacketCustom
import cpw.mods.fml.common.registry.GameRegistry
import cpw.mods.fml.common.network.NetworkRegistry
import net.minecraft.client.renderer.RenderBlocks
import net.minecraft.init.{Blocks, Items}
class MicroblockProxy_serverImpl
{
private var baseID = 24875-1
def nextItemID = {baseID+=1; baseID}
var itemMicro:ItemMicroPart = _
var sawStone:Item = _
var sawIron:Item = _
var sawDiamond:Item = _
var stoneRod:Item = _
def preInit()
{
itemMicro = new ItemMicroPart
GameRegistry.registerItem(itemMicro, "microblock")
sawStone = createSaw(config, "sawStone", 1)
sawIron = createSaw(config, "sawIron", 2)
sawDiamond = createSaw(config, "sawDiamond", 3)
stoneRod = new Item().setUnlocalizedName("microblock:stoneRod").setTextureName("microblock:stoneRod")
GameRegistry.registerItem(stoneRod, "stoneRod")
OreDictionary.registerOre("rodStone", stoneRod)
MinecraftForge.EVENT_BUS.register(MicroblockEventHandler)
}
def createSaw(config:ConfigFile, name:String, strength:Int) = {
val saw = new ItemSaw(config.getTag(name).useBraces(), strength).setUnlocalizedName("microblock:"+name)
GameRegistry.registerItem(saw, name)
saw
}
def addSawRecipe(saw:Item, blade:Item)
{
CraftingManager.getInstance.getRecipeList.asInstanceOf[JList[IRecipe]].add(
new ShapedOreRecipe(new ItemStack(saw),
"srr",
"sbr",
's':Character, Items.stick,
'r':Character, "rodStone",
'b':Character, blade))
}
def init()
{
CraftingManager.getInstance.getRecipeList.asInstanceOf[JList[IRecipe]].add(MicroRecipe)
CraftingManager.getInstance.addRecipe(new ItemStack(stoneRod, 4), "s", "s", 's':Character, Blocks.stone)
addSawRecipe(sawStone, Items.flint)
addSawRecipe(sawIron, Items.iron_ingot)
addSawRecipe(sawDiamond, Items.diamond)
}
def postInit()
{
MicroMaterialRegistry.loadIcons()
MicroMaterialRegistry.calcMaxCuttingStrength()
PacketCustom.assignHandshakeHandler(MicroblockSPH.registryChannel, MicroblockSPH)
}
}
class MicroblockProxy_clientImpl extends MicroblockProxy_serverImpl
{
@SideOnly(Side.CLIENT)
private var _renderBlocks:RenderBlocks = _
@SideOnly(Side.CLIENT)
def renderBlocks =
{
if(_renderBlocks == null)
_renderBlocks = new RenderBlocks
_renderBlocks
}
@SideOnly(Side.CLIENT)
override def postInit()
{
super.postInit()
MinecraftForgeClient.registerItemRenderer(itemMicro, ItemMicroPartRenderer)
PacketCustom.assignHandler(MicroblockCPH.registryChannel, MicroblockCPH)
}
@SideOnly(Side.CLIENT)
override def createSaw(config:ConfigFile, name:String, strength:Int) =
{
val saw = super.createSaw(config, name, strength)
MinecraftForgeClient.registerItemRenderer(saw, ItemSawRenderer)
saw
}
}
object MicroblockProxy extends MicroblockProxy_clientImpl
{
}
-300
View File
@@ -1,300 +0,0 @@
package codechicken.multipart
import java.util.List
import java.lang.Iterable
import net.minecraft.block.Block
import net.minecraft.block.material.Material
import net.minecraft.entity.Entity
import net.minecraft.util.AxisAlignedBB
import net.minecraft.world.World
import net.minecraftforge.common.util.ForgeDirection
import net.minecraft.util.Vec3
import net.minecraft.util.MovingObjectPosition
import codechicken.lib.raytracer.RayTracer
import net.minecraft.entity.player.EntityPlayer
import java.util.Random
import java.util.ArrayList
import net.minecraft.item.ItemStack
import net.minecraft.client.particle.EffectRenderer
import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.texture.IIconRegister
import codechicken.lib.render.TextureUtils
import net.minecraft.world.IBlockAccess
import codechicken.lib.raytracer.ExtendedMOP
import scala.collection.JavaConversions._
object BlockMultipart
{
def getTile(world:IBlockAccess, x:Int, y:Int, z:Int):TileMultipart =
{
val tile = world.getTileEntity(x, y, z)
if(tile.isInstanceOf[TileMultipart])
tile.asInstanceOf[TileMultipart]
else
null
}
def getClientTile(world:IBlockAccess, x:Int, y:Int, z:Int):TileMultipartClient =
{
val tile = world.getTileEntity(x, y, z)
if(tile.isInstanceOf[TileMultipartClient])
tile.asInstanceOf[TileMultipartClient]
else
null
}
def reduceMOP(hit:MovingObjectPosition):(Int, ExtendedMOP) = {
val ehit = hit.asInstanceOf[ExtendedMOP]
val data:(Int, _) = ExtendedMOP.getData(hit)
return (data._1, new ExtendedMOP(ehit, data._2, ehit.dist))
}
def drawHighlight(world:World, player:EntityPlayer, hit:MovingObjectPosition, frame:Float):Boolean =
{
val tile = getTile(world, hit.blockX, hit.blockY, hit.blockZ)
if(tile == null)
return false
val (index, mop) = reduceMOP(hit)
if(tile.partList(index).drawHighlight(mop, player, frame))
return true
tile.partList(index).collisionRayTrace(RayTracer.getStartVec(player), RayTracer.getEndVec(player))
return false
}
}
/**
* Block class for all multiparts, should be internal use only.
*/
class BlockMultipart extends Block(Material.rock)
{
import BlockMultipart._
override def hasTileEntity(meta:Int = 0) = true
override def isBlockSolid(world:IBlockAccess, x:Int, y:Int, z:Int, side:Int):Boolean =
getTile(world, x, y, z) match {
case null => false
case tile => tile.isSolid(side)
}
override def onNeighborBlockChange(world:World, x:Int, y:Int, z:Int, block:Block) {
val tile = getTile(world, x, y, z)
if(tile != null)
tile.onNeighborBlockChange()
}
override def collisionRayTrace(world:World, x:Int, y:Int, z:Int, start:Vec3, end:Vec3):ExtendedMOP =
getTile(world, x, y, z) match {
case null => null
case tile => tile.collisionRayTrace(start, end)
}
def rayTraceAll(world:World, x:Int, y:Int, z:Int, start:Vec3, end:Vec3):Iterable[ExtendedMOP] =
getTile(world, x, y, z) match {
case null => Seq()
case tile => tile.rayTraceAll(start, end)
}
override def removedByPlayer(world:World, player:EntityPlayer, x:Int, y:Int, z:Int):Boolean = {
val hit = RayTracer.retraceBlock(world, player, x, y, z)
val tile = getTile(world, x, y, z)
if(hit == null || tile == null)
{
dropAndDestroy(world, x, y, z)
return true
}
val (index, mop) = reduceMOP(hit)
if(world.isRemote)
{
tile.partList(index).addDestroyEffects(mop, Minecraft.getMinecraft.effectRenderer)
return true
}
tile.harvestPart(index, mop, player)
return world.getTileEntity(x, y, z) == null
}
def dropAndDestroy(world:World, x:Int, y:Int, z:Int) {
val tile = getTile(world, x, y, z)
if(tile != null && !world.isRemote)
tile.dropItems(getDrops(world, x, y, z, 0, 0))
world.setBlockToAir(x, y, z)
}
override def quantityDropped(meta:Int, fortune:Int, random:Random) = 0
override def getDrops(world:World, x:Int, y:Int, z:Int, meta:Int, fortune:Int):ArrayList[ItemStack] = {
val ai = new ArrayList[ItemStack]()
if(world.isRemote)
return ai
val tile = getTile(world, x, y, z)
if(tile != null)
tile.partList.foreach(part => part.getDrops.foreach(item => ai.add(item)))
return ai
}
override def addCollisionBoxesToList(world:World, x:Int, y:Int, z:Int, ebb:AxisAlignedBB, list$:List[_], entity:Entity) {
val list = list$.asInstanceOf[List[AxisAlignedBB]]
val tile = getTile(world, x, y, z)
if(tile != null)
tile.partList.foreach(part =>
part.getCollisionBoxes.foreach{c =>
val aabb = c.toAABB.offset(x, y, z)
if(aabb.intersectsWith(ebb))
list.add(aabb)
})
}
override def addHitEffects(world:World, hit:MovingObjectPosition, effectRenderer:EffectRenderer):Boolean = {
val tile = getClientTile(world, hit.blockX, hit.blockY, hit.blockZ)
if(tile != null) {
val (index, mop) = reduceMOP(hit)
if(index < tile.partList.size)
tile.partList(index).addHitEffects(mop, effectRenderer)
}
return true
}
override def addDestroyEffects(world:World, x:Int, y:Int, z:Int, s:Int, effectRenderer:EffectRenderer) = true
override def renderAsNormalBlock() = false
override def isOpaqueCube = false
override def getRenderType = TileMultipart.renderID
override def isAir(world:IBlockAccess, x:Int, y:Int, z:Int):Boolean =
getTile(world, x, y, z) match {
case null => true
case tile => tile.partList.isEmpty
}
override def isReplaceable(world:IBlockAccess, x:Int, y:Int, z:Int) = isAir(world, x, y, z)
override def getRenderBlockPass = 1
override def canRenderInPass(pass:Int):Boolean = {
MultipartRenderer.pass = pass
return true
}
override def getPickBlock(hit:MovingObjectPosition, world:World, x:Int, y:Int, z:Int):ItemStack = {
val tile = getTile(world, x, y, z)
if(tile != null)
{
val (index, mop) = reduceMOP(hit)
return tile.partList(index).pickItem(mop)
}
return null
}
override def getPlayerRelativeBlockHardness(player:EntityPlayer, world:World, x:Int, y:Int, z:Int):Float = {
val hit = RayTracer.retraceBlock(world, player, x, y, z)
val tile = getTile(world, x, y, z)
if(hit != null && tile != null) {
val (index, mop) = reduceMOP(hit)
return tile.partList(index).getStrength(mop, player)/30F
}
return 1/100F
}
/**
* Kludge to set PROTECTED blockIcon to a blank icon
*/
override def registerBlockIcons(register:IIconRegister) {
val icon = TextureUtils.getBlankIcon(16, register)
setBlockTextureName(icon.getIconName)
super.registerBlockIcons(register)
}
override def getLightValue(world:IBlockAccess, x:Int, y:Int, z:Int):Int =
getTile(world, x, y, z) match {
case null => 0
case tile => tile.getLightValue
}
override def randomDisplayTick(world:World, x:Int, y:Int, z:Int, random:Random) {
val tile = getClientTile(world, x, y, z)
if(tile != null)
tile.randomDisplayTick(random)
}
override def onBlockActivated(world:World, x:Int, y:Int, z:Int, player:EntityPlayer, side:Int, hitX:Float, hitY:Float, hitZ:Float):Boolean = {
val hit = RayTracer.retraceBlock(world, player, x, y, z)
if(hit == null)
return false
val tile = getTile(world, x, y, z)
if(tile == null)
return false
val (index, mop) = reduceMOP(hit)
return tile.partList(index).activate(player, mop, player.getHeldItem)
}
override def onBlockClicked(world:World, x:Int, y:Int, z:Int, player:EntityPlayer) {
val hit = RayTracer.retraceBlock(world, player, x, y, z)
if(hit == null)
return
val tile = getTile(world, x, y, z)
if(tile == null)
return
val (index, mop) = reduceMOP(hit)
tile.partList(index).click(player, mop, player.getHeldItem)
}
override def isProvidingStrongPower(world:IBlockAccess, x:Int, y:Int, z:Int, side:Int):Int =
getTile(world, x, y, z) match {
case null => 0
case tile => tile.strongPowerLevel(side^1)
}
override def isProvidingWeakPower(world:IBlockAccess, x:Int, y:Int, z:Int, side:Int):Int =
getTile(world, x, y, z) match {
case null => 0
case tile => tile.weakPowerLevel(side^1)
}
override def canConnectRedstone(world:IBlockAccess, x:Int, y:Int, z:Int, side:Int):Boolean =
getTile(world, x, y, z) match {
case null => false
case tile => tile.canConnectRedstone(side)
}
override def onEntityCollidedWithBlock(world:World, x:Int, y:Int, z:Int, entity:Entity) {
val tile = getTile(world, x, y, z)
if(tile != null)
tile.onEntityCollision(entity)
}
override def onNeighborChange(world:IBlockAccess, x:Int, y:Int, z:Int, tileX:Int, tileY:Int, tileZ:Int) {
val tile = getTile(world, x, y, z)
if(tile != null)
tile.onNeighborTileChange(tileX, tileY, tileZ)
}
override def getExplosionResistance(entity:Entity, world:World, x:Int, y:Int, z:Int, explosionX:Double, explosionY:Double, explosionZ:Double):Float =
getTile(world, x, y, z) match {
case null => 0
case tile => tile.getExplosionResistance(entity)
}
override def getWeakChanges(world:IBlockAccess, x:Int, y:Int, z:Int) =
getTile(world, x, y, z) match {
case null => false
case tile => tile.getWeakChanges()
}
override def canProvidePower = true
}
@@ -1,58 +0,0 @@
package codechicken.multipart
import net.minecraft.client.settings.KeyBinding
import org.lwjgl.input.Keyboard
import net.minecraft.client.Minecraft
import codechicken.lib.packet.PacketCustom
import codechicken.multipart.handler.MultipartCPH
import scala.collection.mutable.HashMap
import net.minecraft.entity.player.EntityPlayer
import cpw.mods.fml.common.eventhandler.SubscribeEvent
import cpw.mods.fml.common.gameevent.InputEvent.KeyInputEvent
import cpw.mods.fml.relauncher.{Side, SideOnly}
import cpw.mods.fml.common.gameevent.TickEvent.ClientTickEvent
/**
* A class that maintains a map server<->client of which players are holding the control (or placement modifier key) much like sneaking.
*/
object ControlKeyModifer
{
implicit def playerControlValue(p:EntityPlayer) = new ControlKeyValue(p)
class ControlKeyValue(p:EntityPlayer)
{
def isControlDown = map(p)
}
val map = HashMap[EntityPlayer, Boolean]().withDefaultValue(false)
/**
* Implicit static for Java users.
*/
def isControlDown(p:EntityPlayer) = p.isControlDown
}
/**
* Key Handler implementation
*/
object ControlKeyHandler extends KeyBinding("key.control", Keyboard.KEY_LCONTROL, "key.categories.gameplay")
{
import ControlKeyModifer._
var wasPressed = false
@SubscribeEvent
@SideOnly(Side.CLIENT)
def tick(event:ClientTickEvent) {
val pressed = getIsKeyPressed
if(pressed != wasPressed) {
wasPressed = pressed
if(Minecraft.getMinecraft.getNetHandler != null)
{
map.put(Minecraft.getMinecraft.thePlayer, pressed)
val packet = new PacketCustom(MultipartCPH.channel, 1)
packet.writeBoolean(pressed)
packet.sendToServer()
}
}
}
}
-33
View File
@@ -1,33 +0,0 @@
package codechicken.multipart
import codechicken.lib.data.MCDataOutput
import codechicken.lib.data.MCDataInput
/**
* Class for reading and writing ids, widening the carrier data type as necessary
*/
class IDWriter
{
var write:(MCDataOutput, Int)=>Unit = _
var read:(MCDataInput)=>Int = _
def setMax(i:Int)
{
val l = i.toLong & 0xFFFFFFFF
if(l > 0xFFFF)
{
write = (data, i) => data.writeInt(i)
read = (data) => data.readInt()
}
else if(l > 0xFF)
{
write = (data, i) => data.writeShort(i)
read = (data) => data.readUShort()
}
else
{
write = (data, i) => data.writeByte(i)
read = (data) => data.readUByte()
}
}
}
@@ -1,15 +0,0 @@
package codechicken.multipart
/**
* Mixin interface for parts that want to be notified of neighbor tile change events (comparators or inventory maintainers)
*/
trait INeighborTileChange {
/**
* Returns whether this part needs calls for tile changes through one solid block
*/
def weakTileChanges():Boolean
/**
* Callback for neighbor tile changes, from same function in Block
*/
def onNeighborTileChanged(side:Int, weak:Boolean)
}
@@ -1,16 +0,0 @@
package codechicken.multipart
import java.util.Random
/**
* Interface for parts that need random display ticks (torches)
*
* Marker interface for TRandomDisplayTickTile
*/
trait IRandomDisplayTick
{
/**
* Called on a random display tick.
*/
def randomDisplayTick(random:Random)
}
@@ -1,12 +0,0 @@
package codechicken.multipart
/**
* Interface for parts with random update ticks.
*/
trait IRandomUpdateTick
{
/**
* Called on random update. Random ticks are between 800 and 1600 ticks from their last scheduled/random tick
*/
def randomUpdate()
}
-243
View File
@@ -1,243 +0,0 @@
package codechicken.multipart
import net.minecraft.world.World
import net.minecraft.block.Block
import net.minecraft.world.IBlockAccess
import net.minecraft.util.Direction
import codechicken.lib.vec.Rotation._
import net.minecraft.init.Blocks
/**
* Interface for parts with redstone interaction
*
* Marker interface for TRedstoneTile. This means that if a part is an instance of IRedstonePart, the container tile may be cast to TRedstoneTile
*/
trait IRedstonePart
{
/**
* Returns the strong (indirect, through blocks) signal being emitted by this part to the specified side
*/
def strongPowerLevel(side:Int):Int
/**
* Returns the weak (direct) signal being emitted by this part to the specified side
*/
def weakPowerLevel(side:Int):Int
/**
* Returns true if this part can connect to redstone on the specified side. Blocking parts like covers will be handled by RedstoneInteractions
*/
def canConnectRedstone(side:Int):Boolean
}
/**
* For parts like wires that adhere to a specific face, reduces redstone connections to the specific edge between two faces.
* Should be implemented on parts implementing TFacePart
*/
trait IFaceRedstonePart extends IRedstonePart
{
/**
* Return the face to which this redstone part is attached
*/
def getFace:Int
}
/**
* For parts that want to define their own connection masks (like center-center parts)
*/
trait IMaskedRedstonePart extends IRedstonePart
{
/**
* Returns the redstone connection mask for this part on side. See IRedstoneConnector for mask definition
*/
def getConnectionMask(side:Int):Int
}
/**
* Interface for tile entities which split their redstone connections into a mask for each side (edges and center)
*
* All connection masks are a 5 bit map.
* The lowest 4 bits correspond to the connection toward the face specified Rotation.rotateSide(side&6, b) where b is the bit index from lowest to highest.
* Bit 5 corresponds to a connection opposite side.
*/
trait IRedstoneConnector
{
/**
* Returns the redstone connection mask for this tile on side.
*/
def getConnectionMask(side:Int):Int
/**
* Returns the weak power level provided by this tile on side through mask
*/
def weakPowerLevel(side:Int, mask:Int):Int
}
/**
* Internal interface for TileMultipart instances hosting IRedstonePart
*/
trait IRedstoneTile extends IRedstoneConnector
{
/**
* Returns a mask of spaces through which a wire could connect on side
*/
def openConnections(side:Int):Int
}
/**
* Block version of IRedstoneConnector
* Due to the inadequate Block.canConnectRedstone not handling the bottom side (nor the top particularly well)
*/
trait IRedstoneConnectorBlock
{
def getConnectionMask(world:IBlockAccess, x:Int, y:Int, z:Int, side:Int):Int
def weakPowerLevel(world:IBlockAccess, x:Int, y:Int, z:Int, side:Int, mask:Int):Int
}
/**
* static helper class for calculating various things about redstone.
* Indirect power (also known as strong power) is not handled here, just use world.getIndirectPowerTo
* Masks are defined in IRedstoneConnector
*/
object RedstoneInteractions
{
import net.minecraft.util.Facing._
val vanillaSideMap = Array(-2, -1, 0, 2, 3, 1)
val sideVanillaMap = Array(1, 2, 5, 3, 4)
/**
* Hardcoded vanilla overrides for Block.canConnectRedstone (see @IRedstoneConnectorBlock)
*/
val fullVanillaBlocks = Set(
Blocks.redstone_torch,
Blocks.unlit_redstone_torch,
Blocks.lever,
Blocks.stone_button,
Blocks.wooden_button,
Blocks.redstone_block)
/**
* Get the direct power to p on side
*/
def getPowerTo(p:TMultiPart, side:Int):Int =
{
val tile = p.tile
return getPowerTo(tile.getWorldObj, tile.xCoord, tile.yCoord, tile.zCoord, side,
tile.asInstanceOf[IRedstoneTile].openConnections(side)&connectionMask(p, side))
}
/**
* Get the direct power level to space (x, y, z) on side with mask
*/
def getPowerTo(world:World, x:Int, y:Int, z:Int, side:Int, mask:Int):Int =
getPower(world, x+offsetsXForSide(side), y+offsetsYForSide(side), z+offsetsZForSide(side), side^1, mask)
/**
* Get the direct power level provided by space (x, y, z) on side with mask
*/
def getPower(world:World, x:Int, y:Int, z:Int, side:Int, mask:Int):Int =
{
val tile = world.getTileEntity(x, y, z)
if(tile.isInstanceOf[IRedstoneConnector])
return tile.asInstanceOf[IRedstoneConnector].weakPowerLevel(side, mask)
val block = world.getBlock(x, y, z)
if(block.isInstanceOf[IRedstoneConnectorBlock])
return block.asInstanceOf[IRedstoneConnectorBlock].weakPowerLevel(world, x, y, z, side, mask)
val vmask = vanillaConnectionMask(block, world, x, y, z, side, true)
if((vmask&mask) > 0)
{
var m = world.getIndirectPowerLevelTo(x, y, z, side^1)
if(m < 15 && block == Blocks.redstone_wire)
m = Math.max(m, world.getBlockMetadata(x, y, z))//painful vanilla kludge
return m
}
return 0
}
def vanillaToSide(vside:Int) = sideVanillaMap(vside+1)
/**
* Get the connection mask of the block on side of (x, y, z).
* @param power, whether the connection mask is for signal transfer or visual connection. (some blocks accept power without visual connection)
*/
def otherConnectionMask(world:IBlockAccess, x:Int, y:Int, z:Int, side:Int, power:Boolean):Int =
getConnectionMask(world, x+offsetsXForSide(side), y+offsetsYForSide(side), z+offsetsZForSide(side), side^1, power)
/**
* Get the connection mask of part on side
*/
def connectionMask(p:TMultiPart, side:Int):Int =
{
if(p.isInstanceOf[IRedstonePart] && p.asInstanceOf[IRedstonePart].canConnectRedstone(side))
{
if(p.isInstanceOf[IFaceRedstonePart])
{
val fside = p.asInstanceOf[IFaceRedstonePart].getFace
if((side&6) == (fside&6))
return 0x10
return 1<<rotationTo(side&6, fside)
}
else if(p.isInstanceOf[IMaskedRedstonePart])
{
return p.asInstanceOf[IMaskedRedstonePart].getConnectionMask(side)
}
return 0x1F
}
return 0
}
/**
* @param power If true, don't test canConnectRedstone on blocks, just get a power transmission mask rather than a visual connection
*/
def getConnectionMask(world:IBlockAccess, x:Int, y:Int, z:Int, side:Int, power:Boolean):Int =
{
val tile = world.getTileEntity(x, y, z)
if(tile.isInstanceOf[IRedstoneConnector])
return tile.asInstanceOf[IRedstoneConnector].getConnectionMask(side)
val block = world.getBlock(x, y, z)
if(block.isInstanceOf[IRedstoneConnectorBlock])
return block.asInstanceOf[IRedstoneConnectorBlock].getConnectionMask(world, x, y, z, side)
return vanillaConnectionMask(block, world, x, y, z, side, power)
}
/**
* Returns the connection mask for a vanilla block
*/
def vanillaConnectionMask(block:Block, world:IBlockAccess, x:Int, y:Int, z:Int, side:Int, power:Boolean):Int =
{
if(fullVanillaBlocks(block))
return 0x1F
if(side == 0)//vanilla doesn't handle side 0
return if(power) 0x1F else 0
/**
* so that these can be conducted to from face parts on the other side of the block.
* Due to vanilla's in adequecy with redstone/logic on walls
*/
if(block == Blocks.redstone_wire || block == Blocks.powered_comparator || block == Blocks.unpowered_comparator) {
if(side != 0)
return if(power) 0x1F else 4
return 0
}
val vside = vanillaSideMap(side)
if(block == Blocks.powered_repeater || block == Blocks.unpowered_repeater)//stupid minecraft hardcodes
{
val meta = world.getBlockMetadata(x, y, z)
if(vside == (meta & 3) || vside == Direction.rotateOpposite(meta & 3))
return if(power) 0x1F else 4
return 0
}
if(power || block.canConnectRedstone(world, x, y, z, vside))//some blocks accept power without visualising connections
return 0x1F
return 0
}
}
-54
View File
@@ -1,54 +0,0 @@
package codechicken.multipart
import net.minecraft.item.Item
import codechicken.lib.vec.Vector3
import codechicken.lib.vec.Rotation
import net.minecraft.item.ItemStack
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.world.World
import codechicken.lib.vec.BlockCoord
/**
* Java class implementation
*/
abstract class JItemMultiPart extends Item with TItemMultiPart
/**
* Simple multipart item class for easy placement. Simply override the newPart function and it the part will be added to the block space if it passes the occlusion tests.
*/
trait TItemMultiPart extends Item
{
def getHitDepth(vhit:Vector3, side:Int):Double =
vhit.copy.scalarProject(Rotation.axes(side)) + (side%2^1)
override def onItemUse(item:ItemStack, player:EntityPlayer, world:World, x:Int, y:Int, z:Int, side:Int, hitX:Float, hitY:Float, hitZ:Float):Boolean =
{
val pos = new BlockCoord(x, y, z)
val vhit = new Vector3(hitX, hitY, hitZ)
val d = getHitDepth(vhit, side)
def place():Boolean =
{
val part = newPart(item, player, world, pos, side, vhit)
if(part == null || !TileMultipart.canPlacePart(world, pos, part))
return false
if(!world.isRemote)
TileMultipart.addPart(world, pos, part)
if(!player.capabilities.isCreativeMode)
item.stackSize-=1
return true
}
if(d < 1 && place())
return true
pos.offset(side)
return place()
}
/**
* Create a new part based on the placement information parameters.
*/
def newPart(item:ItemStack, player:EntityPlayer, world:World, pos:BlockCoord, side:Int, vhit:Vector3):TMultiPart
}
@@ -1,183 +0,0 @@
package codechicken.multipart
import scala.collection.mutable.{Map => MMap}
import codechicken.lib.packet.PacketCustom
import codechicken.lib.data.MCDataOutput
import codechicken.lib.data.MCDataInput
import net.minecraft.world.World
import codechicken.lib.vec.BlockCoord
import scala.collection.mutable.ListBuffer
import cpw.mods.fml.common.ModContainer
import cpw.mods.fml.common.Loader
import net.minecraft.block.Block
import java.lang.Iterable
import com.google.common.collect.ArrayListMultimap
import scala.collection.JavaConversions._
/**
* This class handles the registration and internal ID mapping of all multipart classes.
*/
object MultiPartRegistry
{
/**
* Interface to be registered for constructing parts.
* Every instance of every multipart is constructed from an implementor of this.
*/
trait IPartFactory
{
/**
* Create a new instance of the part with the specified type name identifier
* @param client If the part instance is for the client or the server
*/
def createPart(name: String, client: Boolean): TMultiPart
}
/**
* An interface for converting existing blocks/tile entities to multipart versions.
*/
trait IPartConverter
{
/**
* Return true if this converter can handle the specific blockID (may or may not actually convert the block)
*/
def blockTypes: Iterable[Block]
/**
* Return a multipart version of the block at pos in world. Return null if no conversion is possible.
*/
def convert(world: World, pos: BlockCoord): TMultiPart
}
private val typeMap = MMap[String, (Boolean) => TMultiPart]()
private val nameMap = MMap[String, Int]()
private var idMap: Array[(String, (Boolean) => TMultiPart)] = _
private val idWriter = new IDWriter
private val converters = ArrayListMultimap.create[Block, IPartConverter]()
private val containers = MMap[String, ModContainer]()
/**
* The state of the registry. 0 = no parts, 1 = registering, 2 = registered
*/
private var state: Int = 0
/**
* Register a part factory with an array of types it is capable of instantiating. Must be called before postInit
*/
def registerParts(partFactory: IPartFactory, types: Array[String]) {
registerParts(partFactory.createPart _, types: _*)
}
/**
* Scala function version of registerParts
*/
def registerParts(partFactory: (String, Boolean) => TMultiPart, types: String*) {
if (loaded)
throw new IllegalStateException("Parts must be registered in the init methods.")
state = 1
val container = Loader.instance.activeModContainer
if (container == null)
throw new IllegalStateException("Parts must be registered during the initialization phase of a mod container")
types.foreach {
s =>
if (typeMap.contains(s))
throw new IllegalStateException("Part with id " + s + " is already registered.")
typeMap.put(s, (c: Boolean) => partFactory(s, c))
containers.put(s, container)
}
}
/**
* Register a part converter instance
*/
def registerConverter(c: IPartConverter) {
c.blockTypes.foreach(converters.put(_, c))
}
private[multipart] def beforeServerStart() {
idMap = typeMap.toList.sortBy(_._1).toArray
idWriter.setMax(idMap.length)
nameMap.clear()
for (i <- 0 until idMap.length)
nameMap.put(idMap(i)._1, i)
}
private[multipart] def writeIDMap(packet: PacketCustom) {
packet.writeInt(idMap.length)
idMap.foreach(e => packet.writeString(e._1))
}
private[multipart] def readIDMap(packet: PacketCustom): Seq[String] = {
val k = packet.readInt()
idWriter.setMax(k)
idMap = new Array(k)
nameMap.clear()
val missing = ListBuffer[String]()
for (i <- 0 until k) {
val s = packet.readString()
val v = typeMap.get(s)
if (v.isEmpty)
missing += s
else {
idMap(i) = (s, v.get)
nameMap.put(s, i)
}
}
return missing
}
/**
* Return true if any multiparts have been registered
*/
private[multipart] def required = state > 0
/**
* Return true if no more parts can be registered
*/
def loaded = state == 2
private[multipart] def postInit() {
state = 2
}
/**
* Writes the id of part to data
*/
def writePartID(data: MCDataOutput, part: TMultiPart) {
idWriter.write(data, nameMap.get(part.getType).get)
}
/**
* Uses instantiators to creat a new part from the id read from data
*/
def readPart(data: MCDataInput) = idMap(idWriter.read(data))._2(true)
/**
* Uses instantiators to create a new part with specified identifier on side
*/
def createPart(name: String, client: Boolean): TMultiPart = {
val part = typeMap.get(name)
if (part.isDefined)
return part.get(client)
else {
System.err.println("Missing mapping for part with ID: " + name)
return null
}
}
/**
* Calls converters to create a multipart version of the block at pos
*/
def convertBlock(world: World, pos: BlockCoord, block: Block): TMultiPart = {
for (c <- converters.get(block)) {
val ret = c.convert(world, pos)
if (ret != null)
return ret
}
return null
}
def getModContainer(name: String) = containers(name)
}
@@ -1,219 +0,0 @@
package codechicken.multipart
import net.minecraft.tileentity.TileEntity
import scala.collection.immutable.Map
import net.minecraft.world.World
import codechicken.lib.vec.BlockCoord
import codechicken.multipart.handler.MultipartProxy
import codechicken.lib.packet.PacketCustom
import codechicken.multipart.asm.IMultipartFactory
import codechicken.multipart.asm.ASMMixinFactory
import net.minecraft.network.play.server.S23PacketBlockChange
/**
* This class manages the dynamic construction and allocation of container TileMultipart instances.
*
* Classes that extend TileMultipart, adding tile centric logic, optimisations or interfaces, can be registered to a marker interface on a part instance.
* When a part is added to the tile that implements the certain marker interface, the container tile will be replaced with a class that includes the functionality from the corresponding mixin class.
*
* Classes are generated in a similar fashion to the way scala traits are compiled. To see the output, simply enable the config option and look in the asm/multipart folder of you .minecraft directory.
*
* There are several mixin traits that come with the API included in the scalatraits package. TPartialOcclusionTile is defined as class instead of trait to give an example for Java programmers.
*/
object MultipartGenerator
{
private var tileTraitMap:Map[Class[_], Set[String]] = Map()
private var interfaceTraitMap_c:Map[String, String] = Map()
private var interfaceTraitMap_s:Map[String, String] = Map()
private var partTraitMap_c:Map[Class[_], Seq[String]] = Map()
private var partTraitMap_s:Map[Class[_], Seq[String]] = Map()
var factory:IMultipartFactory = ASMMixinFactory
def partTraitMap(client:Boolean) = if(client) partTraitMap_c else partTraitMap_s
def interfaceTraitMap(client:Boolean) = if(client) partTraitMap_c else interfaceTraitMap_s
def traitsForPart(part:TMultiPart, client:Boolean):Seq[String] =
{
var ret = partTraitMap(client).getOrElse(part.getClass, null)
if(ret == null)
{
def heirachy(clazz:Class[_]):Seq[Class[_]] =
{
var superClasses:Seq[Class[_]] = clazz.getInterfaces.flatMap(c => heirachy(c)):+clazz
if(clazz.getSuperclass != null)
superClasses = superClasses++heirachy(clazz.getSuperclass)
return superClasses
}
val interfaceTraitMap = if(client) interfaceTraitMap_c else interfaceTraitMap_s
ret = heirachy(part.getClass).flatMap(c => interfaceTraitMap.get(c.getName)).distinct
if(client)
partTraitMap_c = partTraitMap_c+(part.getClass -> ret)
else
partTraitMap_s = partTraitMap_s+(part.getClass -> ret)
}
return ret
}
/**
* Check if part adds any new interfaces to tile, if so, replace tile with a new copy and call tile.addPart(part)
* returns true if tile was replaced
*/
private[multipart] def addPart(world:World, pos:BlockCoord, part:TMultiPart):TileMultipart =
{
val (tile, converted) = TileMultipart.getOrConvertTile2(world, pos)
var partTraits = traitsForPart(part, world.isRemote)
var ntile = tile
if(ntile != null)
{
if(converted)//perform client conversion
{
ntile.partList(0).invalidateConvertedTile()
world.setBlock(pos.x, pos.y, pos.z, MultipartProxy.block, 0, 0)
silentAddTile(world, pos, ntile)
PacketCustom.sendToChunk(new S23PacketBlockChange(pos.x, pos.y, pos.z, world), world, pos.x>>4, pos.z>>4)
ntile.partList(0).onConverted()
ntile.writeAddPart(ntile.partList(0))
}
val tileTraits = tileTraitMap(tile.getClass)
partTraits = partTraits.filter(!tileTraits(_))
if(!partTraits.isEmpty)
{
ntile = factory.generateTile(partTraits++tileTraits, world.isRemote)
tile.setValid(false)
silentAddTile(world, pos, ntile)
ntile.from(tile)
}
}
else
{
world.setBlock(pos.x, pos.y, pos.z, MultipartProxy.block, 0, 0)
ntile = factory.generateTile(partTraits, world.isRemote)
silentAddTile(world, pos, ntile)
}
ntile.addPart_impl(part)
return ntile
}
/**
* Adds a tile entity to the world without notifying neighbor blocks or adding it to the tick list
*/
def silentAddTile(world:World, pos:BlockCoord, tile:TileEntity)
{
val chunk = world.getChunkFromBlockCoords(pos.x, pos.z)
if(chunk != null)
chunk.func_150812_a(pos.x & 15, pos.y, pos.z & 15, tile)
}
/**
* Check if tile satisfies all the interfaces required by parts. If not, return a new generated copy of tile
*/
private[multipart] def generateCompositeTile(tile:TileEntity, parts:Seq[TMultiPart], client:Boolean):TileMultipart =
{
val partTraits = parts.flatMap(traitsForPart(_, client)).distinct
if(tile != null && tile.isInstanceOf[TileMultipart])
{
val tileTraits = tileTraitMap(tile.getClass)
if(partTraits.forall(tileTraits(_)) && partTraits.size == tileTraits.size)//equal contents
return tile.asInstanceOf[TileMultipart]
}
return factory.generateTile(partTraits, client)
}
/**
* Check if there are any redundant interfaces on tile, if so, replace tile with new copy
*/
private[multipart] def partRemoved(tile:TileMultipart, part:TMultiPart):TileMultipart =
{
val client = tile.getWorldObj.isRemote
val partTraits = tile.partList.flatMap(traitsForPart(_, client))
val testSet = partTraits.toSet
if(!traitsForPart(part, client).forall(testSet(_)))
{
val ntile = factory.generateTile(testSet.toSeq, client)
tile.setValid(false)
silentAddTile(tile.getWorldObj, new BlockCoord(tile), ntile)
ntile.from(tile)
ntile.notifyTileChange()
return ntile
}
return tile
}
/**
* register s_trait to be applied to tiles containing parts implementing s_interface
*/
def registerTrait(s_interface:String, s_trait:String):Unit = registerTrait(s_interface, s_trait, s_trait)
/**
* register traits to be applied to tiles containing parts implementing s_interface
* s_trait for server worlds (may be null)
* c_trait for client worlds (may be null)
*/
def registerTrait(s_interface:String, c_trait:String, s_trait:String)
{
if(c_trait != null)
{
if(interfaceTraitMap_c.contains(s_interface))
System.err.println("Trait already registered for "+s_interface)
else
{
interfaceTraitMap_c = interfaceTraitMap_c+(s_interface->c_trait)
factory.registerTrait(s_interface, c_trait, true)
}
}
if(s_trait != null)
{
if(interfaceTraitMap_s.contains(s_interface))
System.err.println("Trait already registered for "+s_interface)
else
{
interfaceTraitMap_s = interfaceTraitMap_s+(s_interface->s_trait)
factory.registerTrait(s_interface, s_trait, false)
}
}
}
def registerPassThroughInterface(s_interface:String):Unit = registerPassThroughInterface(s_interface, true, true)
/**
* A passthrough interface, is an interface to be implemented on the container tile instance, for which all calls are passed directly to the single implementing part.
* Registering a passthrough interface is equivalent to defining a mixin class as follows.
* 1. field 'impl' which contains the reference to the corresponding part
* 2. occlusionTest is overriden to prevent more than one part with s_interface existing in the block space
* 3. implementing s_interface and passing all calls directly to the part instance.
*
* This allows compatibility with APIs that expect interfaces on the tile entity.
*/
def registerPassThroughInterface(s_interface:String, client:Boolean, server:Boolean)
{
val tType = factory.generatePassThroughTrait(s_interface)
if(tType == null)
return
if(client)
{
if(interfaceTraitMap_c.contains(s_interface))
System.err.println("Trait already registered for "+s_interface)
else
interfaceTraitMap_c = interfaceTraitMap_c+(s_interface->tType)
}
if(server)
{
if(interfaceTraitMap_s.contains(s_interface))
System.err.println("Trait already registered for "+s_interface)
else
interfaceTraitMap_s = interfaceTraitMap_s+(s_interface->tType)
}
}
private[multipart] def registerTileClass(clazz:Class[_ <: TileEntity], traits:Set[String])
{
tileTraitMap=tileTraitMap+(clazz->traits)
MultipartProxy.onTileClassBuilt(clazz)
}
}
@@ -1,68 +0,0 @@
package codechicken.multipart
import net.minecraft.tileentity.TileEntity
import net.minecraft.nbt.NBTTagCompound
import net.minecraft.world.World
import codechicken.multipart.handler.MultipartSaveLoad
import com.google.common.collect.LinkedListMultimap
import scala.collection.JavaConversions._
import codechicken.multipart.handler.MultipartSPH
import net.minecraft.world.WorldServer
import java.util.Arrays
/**
* Static helper class for handling the unusual way that multipart tile entities load from nbt and send description packets
* <br>
* Multipart tile entities will all save themselves with the id "savedMultipart" which if normally loaded by minecraft,
* will create a dummy tile entity which just holds the NBT it was read from. These dummies are then converted to actual container tiles on the ChunkLoad event.
* The createTileFromNBT function should be used to construct a multipart tile from NBT without the ChunkLoad event.
* <br>
* Multipart tile entities do not send description packets via the conventional means of one packet per tile when PlayerInstance calls for it, to do so would be terribly inefficient.
* Instead, the ChunkWatch event is used to batch all the describing data for a chunk into one packet which is compressed using relative positions.
* The sendDescPacket(s) functions should be used to send the description packet of a tile or tiles without a ChunkWatch event.
* <br>
* An example of using this class to move blocks/tile entites around can be found at www.chickenbones.craftsaddle.org/Files/Other/ItemDevTool2.java
*/
object MultipartHelper
{
/*val f_playersInChunk = classOf[PlayerInstance].getDeclaredField(
new ObfMapping("net/minecraft/server/management/PlayerInstance", "playersInChunk", "Ljava/util/List;")
.toRuntime.s_name)
f_playersInChunk.setAccessible(true)
def playersInChunk(inst:PlayerInstance) = f_playersInChunk.get(inst).asInstanceOf[List[_]]*/
def createTileFromNBT(world:World, tag:NBTTagCompound):TileEntity = {
if(!tag.getString("id").equals("savedMultipart"))
return null
MultipartSaveLoad.loadingWorld = world
return TileMultipart.createFromNBT(tag)
}
/**
* Note. This method should only be used to send tiles that have been created on the server mid-game via an NBT load to clients.
*/
def sendDescPacket(world:World, tile:TileEntity) {
val c = world.getChunkFromBlockCoords(tile.xCoord, tile.zCoord)
val pkt = MultipartSPH.getDescPacket(c, Arrays.asList(tile).iterator)
if(pkt != null)
pkt.sendToChunk(world, c.xPosition, c.zPosition)
}
/* Removed for compilation until PlayerInstance access transformer is pulled in forge
def sendDescPackets(world:World, tiles:Iterable[TileEntity]) {
val map = LinkedListMultimap.create[Long, TileEntity]()
tiles.filter(_.isInstanceOf[TileMultipart]).foreach(t => map.put(t.xCoord.toLong<<32|t.zCoord, t))
val mgr = world.asInstanceOf[WorldServer].getPlayerManager
map.asMap.entrySet.foreach{e =>
val coord = e.getKey
val c = world.getChunkFromBlockCoords((coord>>32).toInt, coord.toInt)
lazy val pkt = MultipartSPH.getDescPacket(c, e.getValue.iterator)
val inst = mgr.getOrCreateChunkWatcher(c.xPosition, c.zPosition, false)
if(!inst.playersWatchingChunk.isEmpty)
inst.sendToAllPlayersWatchingChunk(pkt.toPacket)
}
}*/
}
@@ -1,74 +0,0 @@
package codechicken.multipart
import codechicken.lib.render.CCRenderState
import codechicken.lib.vec.Vector3
import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler
import cpw.mods.fml.client.registry.RenderingRegistry
import net.minecraft.block.Block
import net.minecraft.client.renderer.{Tessellator, RenderBlocks}
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer
import net.minecraft.tileentity.TileEntity
import net.minecraft.world.IBlockAccess
import net.minecraft.client.Minecraft
import cpw.mods.fml.relauncher.SideOnly
import cpw.mods.fml.relauncher.Side
import codechicken.lib.raytracer.ExtendedMOP
import codechicken.lib.lighting.LightMatrix
/**
* Internal class for rendering callbacks. Should be moved to the handler package
*/
@SideOnly(Side.CLIENT)
object MultipartRenderer extends TileEntitySpecialRenderer with ISimpleBlockRenderingHandler
{
TileMultipart.renderID = RenderingRegistry.getNextAvailableRenderId
var pass:Int = 0
override def renderTileEntityAt(t:TileEntity, x:Double, y:Double, z:Double, f:Float)
{
val tmpart = t.asInstanceOf[TileMultipartClient]
if(tmpart.partList.isEmpty)
return
CCRenderState.reset()
CCRenderState.pullLightmap()
CCRenderState.useNormals = true
val pos = new Vector3(x, y, z)
tmpart.renderDynamic(pos, f, pass)
}
override def getRenderId = TileMultipart.renderID
override def renderWorldBlock(world:IBlockAccess, x:Int, y:Int, z:Int, block:Block, modelId:Int, renderer:RenderBlocks):Boolean =
{
val t = world.getTileEntity(x, y, z)
if(!t.isInstanceOf[TileMultipartClient])
return false
val tmpart = t.asInstanceOf[TileMultipartClient]
if(tmpart.partList.isEmpty)
return false
if(renderer.hasOverrideBlockTexture)
{
val hit = Minecraft.getMinecraft.objectMouseOver
if(hit != null && hit.blockX == x && hit.blockY == y && hit.blockZ == z && ExtendedMOP.getData(hit).isInstanceOf[(_, _)])
{
val hitInfo:(Int, _) = ExtendedMOP.getData(hit)
if(hitInfo._1.isInstanceOf[Int] && hitInfo._1 >= 0 && hitInfo._1 < tmpart.partList.size)
tmpart.partList(hitInfo._1).drawBreaking(renderer)
}
return false
}
CCRenderState.reset()
CCRenderState.lightMatrix.locate(world, x, y, z)
return tmpart.renderStatic(new Vector3(x, y, z), pass)
}
override def renderInventoryBlock(block:Block, meta:Int, modelId:Int, renderer:RenderBlocks) {}
def shouldRender3DInInventory(modelId:Int) = false
}
@@ -1,82 +0,0 @@
package codechicken.multipart
import scala.collection.mutable.{Map => MMap}
import codechicken.lib.data.MCDataOutput
import codechicken.lib.data.MCDataInput
/**
* Static class for packing update data.
* When a specific property of a part changes and needs sending to the client, a bit can be set in the mask.
* This bit can then be checked in the writeScheduled callback.
* This prevents sending multiple packets if the same property updates more than once per tick.
*/
object PacketScheduler
{
private val map = MMap[TMultiPart, Long]()
/**
* Add bits to the current update mask for part. (binary OR)
*/
def schedulePacket(part:TMultiPart, mask:Long) {
if(part.world.isRemote)
throw new IllegalArgumentException("Cannot use PacketScheduler on a client world")
map.put(part, map.getOrElse(part, 0L)|mask)
}
private[multipart] def sendScheduled() {
map.foreach{ e =>
val (part, mask) = e
if(part.tile != null) {
val ipart = part.asInstanceOf[IScheduledPacketPart]
val w = part.getWriteStream
ipart.maskWidth match {
case 1 => w.writeByte(mask.toInt)
case 2 => w.writeShort(mask.toInt)
case 4 => w.writeInt(mask.toInt)
case 8 => w.writeLong(mask)
}
ipart.writeScheduled(mask, w)
}
}
map.clear()
}
}
/**
* Callback interface for PacketScheduler
*/
trait IScheduledPacketPart
{
/**
* Write scheduled data to the packet, mask is the cumulative mask from calls to schedulePacket
*/
def writeScheduled(mask:Long, packet:MCDataOutput)
/**
* Returns the width (in bytes) of the data type required to hold all valid mask bits. Valid values are 1, 2, 4 and 8
*/
def maskWidth:Int
/**
* Read data matching mask. Estiablishes a method for subclasses to override. This should be called from read
*/
def readScheduled(mask:Long, packet:MCDataInput)
}
trait TScheduledPacketPart extends TMultiPart with IScheduledPacketPart
{
final override def read(packet:MCDataInput) {
val mask = maskWidth match {
case 1 => packet.readUByte
case 2 => packet.readUShort
case 4 => packet.readInt
case 8 => packet.readLong
}
readScheduled(mask, packet)
}
def writeScheduled(mask:Long, packet:MCDataOutput){}
def readScheduled(mask:Long, packet:MCDataInput){}
}
-134
View File
@@ -1,134 +0,0 @@
package codechicken.multipart;
/**
* Defines what each slot in a multipart tile corresponds to and provides some utility functions.
* For performance reasons, it is recommended that integer constants be useds as opposed to this enum
*/
public enum PartMap
{
BOTTOM(0),
TOP(1),
NORTH(2),
SOUTH(3),
WEST(4),
EAST(5),
CENTER(6),
CORNER_NNN(7),//0
CORNER_NPN(8),//1
CORNER_NNP(9),//2
CORNER_NPP(10),//3
CORNER_PNN(11),//4
CORNER_PPN(12),//5
CORNER_PNP(13),//6
CORNER_PPP(14),//7
EDGE_NYN(15),//0
EDGE_NYP(16),//1
EDGE_PYN(17),//2
EDGE_PYP(18),//3
EDGE_NNZ(19),//4
EDGE_PNZ(20),//5
EDGE_NPZ(21),//6
EDGE_PPZ(22),//7
EDGE_XNN(23),//8
EDGE_XPN(24),//9
EDGE_XNP(25),//10
EDGE_XPP(26);//11
public final int i;
public final int mask;
private PartMap(int i)
{
this.i = i;
mask = 1<<i;
}
/**
* Don't actually use this.
*/
public static PartMap face(int i)
{
return values()[i];
}
/**
* Don't actually use this.
*/
public static PartMap edge(int i)
{
return values()[i+15];
}
/**
* Don't actually use this.
*/
public static PartMap corner(int i)
{
return values()[i+7];
}
/**
* Returns a 3 bit mask of the axis xzy that are variable in this edge.
* For example, the first 4 edges (15-18) are along the Y axis, variable in x and z, so the mask is 110b. Note axis y is 1<<0, z is 1<<1 and x is 1<<2
* Note the parameter e is relative to the first edge slot and can range from 0-11
*/
public static int edgeAxisMask(int e)
{
switch(e>>2)
{
case 0: return 6;
case 1: return 5;
case 2: return 3;
}
throw new IllegalArgumentException("Switch Falloff");
}
/**
* Unpacks an edge index, to a mask where high values indicate positive positions in that axis.
* Note the parameter e is relative to the first edge slot and can range from 0-11
* For example, edge 1 (slot 16), is EDGE_NYP so the mask returned would be 010 as z is positive.
*/
public static int unpackEdgeBits(int e)
{
switch(e>>2)
{
case 0: return (e&3)<<1;
case 1: return (e&2)>>1|(e&1)<<2;
case 2: return (e&3);
}
throw new IllegalArgumentException("Switch Falloff");
}
/**
* Repacks a mask of axis bits indicating positive positions, into an edge in along the same axis as e.
* Note the parameter e is relative to the first edge slot and can range from 0-11
*/
public static int packEdgeBits(int e, int bits)
{
switch(e>>2)
{
case 0: return e&0xC|bits>>1;
case 1: return e&0xC|(bits&4)>>2|(bits&1)<<1;
case 2: return e&0xC|bits&3;
}
throw new IllegalArgumentException("Switch Falloff");
}
private static int[] edgeBetweenMap = new int[]{
-1, -1, 8, 10, 4, 5,
-1, -1, 9, 11, 6, 7,
-1, -1,-1, -1, 0, 2,
-1, -1,-1, -1, 1, 3};
/**
* Returns the slot of the edge between 2 sides.
*/
public static int edgeBetween(int s1, int s2)
{
if(s2 < s1)
return edgeBetween(s2, s1);
if((s1&6) == (s2&6))
throw new IllegalArgumentException("Faces "+s1+" and "+s2+" are opposites");
return 15+edgeBetweenMap[s1*6+s2];
}
}
-37
View File
@@ -1,37 +0,0 @@
package codechicken.multipart
import codechicken.lib.vec.Cuboid6
import codechicken.lib.raytracer.IndexedCuboid6
import net.minecraft.client.renderer.RenderBlocks
import codechicken.lib.render.{BlockRenderer, CCRenderState, RenderUtils}
import scala.collection.JavaConversions._
import java.lang.Iterable
import codechicken.lib.vec.Translation
import codechicken.lib.render.uv.IconTransformation
/**
* Java class implementation
*/
abstract class JCuboidPart extends TCuboidPart
/**
* Trait for parts that are simply a cuboid, having one bounding box. Overrides multipart functions to this effect.
*/
trait TCuboidPart extends TMultiPart
{
/**
* Return the bounding Cuboid6 for this part.
*/
def getBounds:Cuboid6
override def getSubParts:Iterable[IndexedCuboid6] = Seq(new IndexedCuboid6(0, getBounds))
override def getCollisionBoxes:Iterable[Cuboid6] = Seq(getBounds)
override def drawBreaking(renderBlocks:RenderBlocks)
{
CCRenderState.reset()
CCRenderState.setPipeline(new Translation(x, y, z), new IconTransformation(renderBlocks.overrideBlockTexture))
BlockRenderer.renderCuboid(getBounds, 0)
}
}
-12
View File
@@ -1,12 +0,0 @@
package codechicken.multipart
/**
* Interface which must be implemented by parts that go in an edge slot.
*/
trait TEdgePart extends TSlottedPart
{
/**
* Return true if this part can conduct redstone signal or let redstone signal pass through it.
*/
def conductsRedstone = false
}
-16
View File
@@ -1,16 +0,0 @@
package codechicken.multipart
/**
* Inteface which must be implemented by parts that go in a face part.
*/
trait TFacePart extends TSlottedPart
{
/**
* Passed down from Block.isBlockSolidOnSide. Return true if this part is solid and opaque on the specified side
*/
def solid(side:Int):Boolean = true
/**
* Return the redstone conduction map for which signal can pass through this part on the face. Eg, hollow covers return 0xF as signal can pass through the center hole.
*/
def redstoneConductionMap = 0
}
@@ -1,81 +0,0 @@
package codechicken.multipart
import codechicken.lib.vec.Cuboid6
import cpw.mods.fml.relauncher.SideOnly
import cpw.mods.fml.relauncher.Side
import net.minecraft.util.IIcon
import net.minecraft.util.MovingObjectPosition
import net.minecraft.client.particle.EffectRenderer
import codechicken.lib.vec.Vector3
import codechicken.lib.raytracer.ExtendedMOP
import codechicken.lib.render.EntityDigIconFX
/**
* This suite of 3 classes provides simple functions for standard minecraft style hit and break particles.
*
* Scala|Java composition setup.
* Due to the lack of mixin inheritance in Java, the classes are structured to suit both languages as follows.
* IconHitEffects contains static implementations of the functions that would be overriden in TMultiPart
* JIconHitEffects is the interface that should be implemented by a Java class,
* which can then override the functions in TMultipart and call the static methods in IconHitEffects with 'this' as the first parameter
* TIconHitEffects is a trait for scala implementors that does includes the overrides/static calls that Java programmers need to include themselves.
*/
object IconHitEffects
{
def addHitEffects(part:JIconHitEffects, hit:MovingObjectPosition, effectRenderer:EffectRenderer)
{
EntityDigIconFX.addBlockHitEffects(part.tile.getWorldObj,
part.getBounds.copy.add(Vector3.fromTileEntity(part.tile)), hit.sideHit,
part.getBreakingIcon(ExtendedMOP.getData(hit), hit.sideHit), effectRenderer)
}
def addDestroyEffects(part:JIconHitEffects, effectRenderer:EffectRenderer)
{
addDestroyEffects(part, effectRenderer, true)
}
def addDestroyEffects(part:JIconHitEffects, effectRenderer:EffectRenderer, scaleDensity:Boolean)
{
val icons = new Array[IIcon](6)
for(i <- 0 until 6)
icons(i) = part.getBrokenIcon(i)
val bounds =
if(scaleDensity) part.getBounds.copy
else Cuboid6.full.copy
EntityDigIconFX.addBlockDestroyEffects(part.tile.getWorldObj,
bounds.add(Vector3.fromTileEntity(part.tile)), icons, effectRenderer)
}
}
/**
* Java interface containing callbacks for particle rendering.
* Make sure to override addHitEffects and addDestroyEffects as in TIconHitEffects
*/
trait JIconHitEffects extends TMultiPart
{
def getBounds:Cuboid6
@SideOnly(Side.CLIENT)
def getBreakingIcon(subPart:Any, side:Int):IIcon = getBrokenIcon(side)
@SideOnly(Side.CLIENT)
def getBrokenIcon(side:Int):IIcon
}
/**
* Trait for scala programmers
*/
trait TIconHitEffects extends JIconHitEffects
{
@SideOnly(Side.CLIENT)
override def addHitEffects(hit:MovingObjectPosition, effectRenderer:EffectRenderer)
{
IconHitEffects.addHitEffects(this, hit, effectRenderer)
}
@SideOnly(Side.CLIENT)
override def addDestroyEffects(effectRenderer:EffectRenderer)
{
IconHitEffects.addDestroyEffects(this, effectRenderer)
}
}
-299
View File
@@ -1,299 +0,0 @@
package codechicken.multipart
import codechicken.lib.vec.Cuboid6
import codechicken.lib.vec.Vector3
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound
import net.minecraft.util.MovingObjectPosition
import codechicken.lib.raytracer.IndexedCuboid6
import cpw.mods.fml.relauncher.SideOnly
import cpw.mods.fml.relauncher.Side
import net.minecraft.client.particle.EffectRenderer
import net.minecraft.client.renderer.RenderBlocks
import codechicken.lib.vec.BlockCoord
import net.minecraft.tileentity.TileEntity
import codechicken.lib.lighting.LightMatrix
import codechicken.lib.data.MCDataOutput
import codechicken.lib.data.MCDataInput
import net.minecraft.entity.Entity
import java.lang.Iterable
import scala.collection.JavaConversions._
import net.minecraft.util.Vec3
import codechicken.lib.raytracer.ExtendedMOP
import codechicken.lib.raytracer.RayTracer
abstract class TMultiPart
{
/**
* Reference to the container TileMultipart instance
*/
var tile:TileMultipart = _
/**
* Legacy helper function for getting the tile entity (from when TileMultipart was a trait). Use tile() now.
*/
@Deprecated
def getTile:TileEntity = tile
/**
* Getter for tile.worldObj
*/
def world = if(tile == null) null else tile.getWorldObj
/**
* Short getter for xCoord
*/
def x = tile.xCoord
/**
* Short getter for yCoord
*/
def y = tile.yCoord
/**
* Short getter for zCoord
*/
def z = tile.zCoord
/**
* The unique string identifier for this class of multipart.
*/
def getType:String
/**
* Called when the container tile instance is changed to update reference
*/
def bind(t:TileMultipart)
{
tile = t
}
/**
* Perform an occlusion test to determine whether this and npart can 'fit' in this block space.
*/
def occlusionTest(npart:TMultiPart):Boolean = true
/**
* Return a list of entity collision boxes.
* Note all Cuboid6's returned by methods in TMultiPart should be within (0,0,0)->(1,1,1)
*/
def getCollisionBoxes:Iterable[Cuboid6] = Seq()
/**
* Perform a raytrace of this part. The default implementation does a Cuboid6 ray trace on bounding boxes returned from getSubParts.
* This should only be overridden if you need special ray-tracing capabilities such as triangular faces.
* The returned ExtendedMOP will be passed to methods such as 'activate' so it is recommended to use the data field to indicate information about the hit area.
*/
def collisionRayTrace(start: Vec3, end: Vec3): ExtendedMOP = {
val offset = new Vector3(x, y, z)
val boxes = getSubParts.map(c => new IndexedCuboid6(c.data, c.copy.add(offset)))
return RayTracer.instance.rayTraceCuboids(new Vector3(start), new Vector3(end), boxes.toList,
new BlockCoord(x, y, z), tile.getBlockType).asInstanceOf[ExtendedMOP]
}
/**
* For the default collisionRayTrace implementation, returns a list of indexed bounding boxes. The data field of ExtendedMOP will be set to the index of the cuboid the raytrace hit.
*/
def getSubParts:Iterable[IndexedCuboid6] = Seq()
/**
* Return a list of items that should be dropped when this part is destroyed.
*/
def getDrops:Iterable[ItemStack] = Seq()
/**
* Return a value indicating how hard this part is to break
* @param hit An instance of ExtendedMOP from collisionRayTrace
*/
def getStrength(hit:MovingObjectPosition, player:EntityPlayer):Float = 1
/**
* Harvest this part, removing it from the container tile and dropping items if necessary.
* @param hit An instance of ExtendedMOP from collisionRayTrace
* @param player The player harvesting the part
*/
def harvest(hit:MovingObjectPosition, player:EntityPlayer)
{
if(!player.capabilities.isCreativeMode)
tile.dropItems(getDrops)
tile.remPart(this)
}
/**
* The light level emitted by this part
*/
def getLightValue = 0
/**
* Explosion resistance of the host tile is the maximum explosion resistance of the contained parts
* @param entity The entity responsible for this explosion
* @return The resistance of this part the the explosion
*/
def explosionResistance(entity:Entity) = 0F
/**
* Add particles and other effects when a player is mining this part
* @param hit An instance of ExtendedMOP from collisionRayTrace
*/
@SideOnly(Side.CLIENT)
def addHitEffects(hit:MovingObjectPosition, effectRenderer:EffectRenderer){}
/**
* Add particles and other effects when a player broke this part
* @param hit An instance of ExtendedMOP from collisionRayTrace
*/
@SideOnly(Side.CLIENT)
def addDestroyEffects(hit:MovingObjectPosition, effectRenderer:EffectRenderer) {addDestroyEffects(effectRenderer)}
@SideOnly(Side.CLIENT)
@Deprecated
def addDestroyEffects(effectRenderer:EffectRenderer){}
/**
* Render the static, unmoving faces of this part into the world renderer.
* The Tessellator is already drawing.
* @param pass The render pass, 1 or 0
* @return true if vertices were added to the tessellator
*/
@SideOnly(Side.CLIENT)
def renderStatic(pos:Vector3, pass:Int) = false
/**
* Render the dynamic, changing faces of this part and other gfx as in a TESR.
* The Tessellator will need to be started if it is to be used.
* @param pos The position of this block space relative to the renderer, same as x, y, z passed to TESR.
* @param frame The partial interpolation frame value for animations between ticks
* @param pass The render pass, 1 or 0
*/
@SideOnly(Side.CLIENT)
def renderDynamic(pos:Vector3, frame:Float, pass:Int){}
/**
* Draw the breaking overlay for this part. The overrideIcon in RenderBlocks will be set to the fracture icon.
*/
@SideOnly(Side.CLIENT)
def drawBreaking(renderBlocks:RenderBlocks){}
/**
* Override the drawing of the selection box around this part.
* @param hit An instance of ExtendedMOP from collisionRayTrace
* @return true if highlight rendering was overridden.
*/
@SideOnly(Side.CLIENT)
def drawHighlight(hit:MovingObjectPosition, player:EntityPlayer, frame:Float):Boolean = false
/**
* @return A Cuboid6 bounding the render of this part for frustum culling. The bounds are relative to the tile coordinates.
*/
def getRenderBounds = Cuboid6.full
/**
* Write all the data required to describe a client version of this part to the packet.
* Called serverside, when a client loads this part for the first time.
*/
def writeDesc(packet:MCDataOutput){}
/**
* Fill out this part with the description information contained in packet. Will be exactly as written from writeDesc.
* Called clientside when a client loads this part for the first time.
*/
def readDesc(packet:MCDataInput){}
/**
* Save part to NBT (only called serverside)
*/
def save(tag:NBTTagCompound){}
/**
* Load part from NBT (only called serverside)
*/
def load(tag:NBTTagCompound){}
/**
* Gets a MCDataOutput instance for writing update data to clients with this part loaded.
* The write stream functions as a buffer which is flushed in a compressed databurst packet at the end of the tick.
*/
def getWriteStream:MCDataOutput = tile.getWriteStream(this)
/**
* Read and operate on data written to getWriteStream. Ensure all data this part wrote is read even if it's not going to be used.
* The default implementation assumes a call to sendDescUpdate as the only use of getWriteStream.
*/
def read(packet:MCDataInput)
{
readDesc(packet)
tile.markRender()
}
/**
* Quick and easy method to re-describe the whole part on the client. This will call read on the client which calls readDesc unless overriden.
* Incremental changes should be sent rather than the whole description packet if possible.
*/
def sendDescUpdate() = writeDesc(getWriteStream)
/**
* Called when a part is added or removed from this block space.
* The part parameter may be null if several things have changed.
*/
def onPartChanged(part:TMultiPart){}
/**
* Called when a neighbor block changed
*/
def onNeighborChanged(){}
/**
* Called when this part is added to the block space
*/
def onAdded() = onWorldJoin()
/**
* Called when this part is removed from the block space
*/
def onRemoved() = onWorldSeparate()
/**
* Called when the containing chunk is loaded on the server.
*/
def onChunkLoad() = onWorldJoin()
/**
* Called when the containing chunk is unloaded on the server.
*/
def onChunkUnload() = onWorldSeparate()
/**
* Called when this part separates from the world (due to removal, chunk unload or other). Use this to sync with external data structures.
*/
def onWorldSeparate(){}
/**
* Called when this part separates from the world (due to removal, chunk unload or other). Use this to sync with external data structures.
*/
def onWorldJoin(){}
/**
* Called when this part is converted from a normal block/tile (only applicable if a converter has been registered)
*/
def onConverted() = onAdded()
/**
* Called when this part is converted from a normal block/tile (only applicable if a converter has been registered) before the original tile has been replaced
* Use this to clear out things like inventory from the old tile.
*/
def invalidateConvertedTile(){}
/**
* Called when this part has been moved without a save/load.
*/
def onMoved() = onWorldJoin()
/**
* Called just before this part is actually removed from the container tile
*/
def preRemove(){}
/**
* Return whether this part needs update ticks. This will only be called on addition/removal so it should be a constant for this instance.
*/
def doesTick = true
/**
* Called once per world tick. This will be called even if doesTick returns false if another part in the space needs ticks.
*/
def update(){}
/**
* Called when a scheduled tick is executed.
*/
def scheduledTick(){}
/**
* Sets a scheduledTick callback for this part ticks in the future. This is a world time value, so if the chunk is unloaded and reloaded some time later, the tick may fire immediately.
*/
def scheduleTick(ticks:Int) = TickScheduler.scheduleTick(this, ticks)
/**
* Return the itemstack for the middle click pick-block function.
*/
def pickItem(hit:MovingObjectPosition):ItemStack = null
/**
* Called on block right click. item is the player's held item.
* This should not modify the part client side. If the client call returns false, the server will not call this function.
* @param hit An instance of ExtendedMOP from collisionRayTrace
*/
def activate(player:EntityPlayer, hit:MovingObjectPosition, item:ItemStack) = false
/**
* Called on block left click. item is the player's held item.
* @param hit An instance of ExtendedMOP from collisionRayTrace
*/
def click(player:EntityPlayer, hit:MovingObjectPosition, item:ItemStack){}
/**
* Called when an entity is within this block space. May not actually collide with this part.
*/
def onEntityCollision(entity:Entity){}
}
@@ -1,68 +0,0 @@
package codechicken.multipart
import codechicken.lib.vec.Cuboid6
import scala.collection.JavaConversions._
import java.lang.Iterable
/**
* This suite of 3 classes provides simple functions for standard bounding box based occlusion testing.
* If any two parts have overlapping bounding boxes, the test fails
*
* See TIconHitEffects for notes on the Scala|Java composition setup.
*/
object NormalOcclusionTest
{
/**
* Performs the test, returns true if the test fails
*/
def apply(boxes1:Traversable[Cuboid6], boxes2:Traversable[Cuboid6]):Boolean =
boxes1.forall(v1 => boxes2.forall(v2 => !v1.intersects(v2)))
/**
* Performs the test, returns true if the test fails
*/
def apply(part1:JNormalOcclusion, part2:TMultiPart):Boolean =
{
var boxes = Seq[Cuboid6]()
if(part2.isInstanceOf[JNormalOcclusion])
boxes = boxes++part2.asInstanceOf[JNormalOcclusion].getOcclusionBoxes
if(part2.isInstanceOf[JPartialOcclusion])
boxes = boxes++part2.asInstanceOf[JPartialOcclusion].getPartialOcclusionBoxes
return NormalOcclusionTest(boxes, part1.getOcclusionBoxes)
}
}
/**
* Java interface containing callbacks for normal occlusion testing.
* Make sure to override occlusionTest as in TNormalOcclusion
*/
trait JNormalOcclusion
{
/**
* Return a list of normal occlusion boxes
*/
def getOcclusionBoxes:Iterable[Cuboid6]
}
/**
* Trait for scala programmers
*/
trait TNormalOcclusion extends TMultiPart with JNormalOcclusion
{
override def occlusionTest(npart:TMultiPart):Boolean =
NormalOcclusionTest(this, npart) && super.occlusionTest(npart)
}
/**
* Utility part class for performing 3rd party occlusion tests
*/
class NormallyOccludedPart(bounds:Iterable[Cuboid6]) extends TMultiPart with TNormalOcclusion
{
def this(bound:Cuboid6) = this(Seq(bound))
def getType = null
def getOcclusionBoxes = bounds
}
@@ -1,76 +0,0 @@
package codechicken.multipart
import codechicken.lib.vec.Cuboid6
import scala.collection.JavaConversions._
import java.lang.Iterable
/**
* This class provides a special type of occlusion model used by microblocks.
* The partial occlusion test defines bounding boxes that may intersect, so long as no part is completely obscured by a combination of the others.
* Partial bounding boxes may not intersect with normal bounding boxes from NormalOcclusionTest
*
* This test is actually managed by the mixin trait TPartialOcclusionTile which is generated when the marker interface JPartialOcclusion is found
*/
class PartialOcclusionTest(size:Int)
{
/**
* The resolution of the test, set to 1/8th of a block
*/
val res = 8
val bits = new Array[Byte](res*res*res)
val partial = new Array[Boolean](size)
def fill(i:Int, part:JPartialOcclusion)
{
fill(i, part.getPartialOcclusionBoxes, part.allowCompleteOcclusion)
}
def fill(i:Int, boxes:Iterable[Cuboid6], complete:Boolean)
{
partial(i) = !complete
boxes.foreach(box => fill(i+1, box))
}
def fill(v:Int, box:Cuboid6)
{
for(x <- (box.min.x*res+0.5).toInt until (box.max.x*res+0.5).toInt)
for(y <- (box.min.y*res+0.5).toInt until (box.max.y*res+0.5).toInt)
for(z <- (box.min.z*res+0.5).toInt until (box.max.z*res+0.5).toInt)
{
val i = (x*res+y)*res+z
if(bits(i) == 0)
bits(i) = v.toByte
else
bits(i) = -1
}
}
def apply():Boolean =
{
val visible = new Array[Boolean](size)
bits.foreach(n => if(n > 0) visible(n-1) = true)
var i = 0
while(i < partial.length)
{
if(partial(i) && !visible(i))
return false
i+=1
}
return true
}
}
trait JPartialOcclusion
{
/**
* Return a list of partial occlusion boxes
*/
def getPartialOcclusionBoxes:Iterable[Cuboid6]
/**
* Return true if this part may be completely obscured
*/
def allowCompleteOcclusion = false
}
-15
View File
@@ -1,15 +0,0 @@
package codechicken.multipart
/**
* Interface for parts that fill a slot based configuration as defined in PartMap.
* If this is implemented, calling partMap(slot) on the host tile will return this part if the corresponding bit in the slotMask is set
*
* Marker interface for TSlottedTile
*/
trait TSlottedPart extends TMultiPart
{
/**
* a bitmask of slots that this part fills. slot x is 1<<x
*/
def getSlotMask:Int
}
-279
View File
@@ -1,279 +0,0 @@
package codechicken.multipart
import codechicken.lib.world.WorldExtensionInstantiator
import codechicken.lib.world.WorldExtension
import codechicken.lib.world.ChunkExtension
import net.minecraft.world.chunk.Chunk
import net.minecraft.world.World
import net.minecraft.nbt.{NBTBase, NBTTagCompound, NBTTagList, CompressedStreamTools}
import scala.collection.mutable.ListBuffer
import codechicken.lib.vec.BlockCoord
import net.minecraft.world.ChunkCoordIntPair
import net.minecraft.world.ChunkPosition
import scala.collection.mutable.HashSet
import java.io.DataOutputStream
import net.minecraftforge.common.DimensionManager
import java.io.File
import java.io.FileOutputStream
import java.io.DataInputStream
import java.io.FileInputStream
import net.minecraft.world.storage.SaveHandler
import java.util.ArrayList
/**
* Used for scheduling delayed callbacks to parts.
* Do not use this for redstone applications that require precise timing.
* If 2 parts are both scheduled for an update on the same tick, there is no guarantee which one will update first.
* These parts should not depend on a state of another part that may have changed before/after them.
*/
object TickScheduler extends WorldExtensionInstantiator
{
class PartTickEntry(val part:TMultiPart, var time:Long, var random:Boolean)
{
def this(part:TMultiPart, ticks:Int) = this(part, ticks, false)
}
private class WorldTickScheduler(world$:World) extends WorldExtension(world$)
{
var schedTime = 0L
var tickChunks = HashSet[ChunkTickScheduler]()
private var processing = false
private val pending = ListBuffer[PartTickEntry]()
def scheduleTick(part:TMultiPart, ticks:Int, random:Boolean)
{
if(processing)
pending+=new PartTickEntry(part, schedTime+ticks, random)
else
_scheduleTick(part, schedTime+ticks, random)
}
def _scheduleTick(part:TMultiPart, time:Long, random:Boolean)
{
if(part.tile != null)
getChunkExtension(part.tile.xCoord>>4, part.tile.zCoord>>4)
.asInstanceOf[ChunkTickScheduler].scheduleTick(part, time, random)
}
def loadRandom(part:TMultiPart) = scheduleTick(part, nextRandomTick, true)
override def preTick()
{
processing = true
}
override def postTick()
{
if(!tickChunks.isEmpty)
tickChunks = tickChunks.filter(_.processTicks())
processing = false
pending.foreach(e => _scheduleTick(e.part, e.time, e.random))
pending.clear()
schedTime+=1
}
def saveDir:File =
{
if(world.provider.dimensionId == 0)//Calling DimensionManager.getCurrentSaveRootDirectory too early breaks game saves, we have a world reference, use it
return world.getSaveHandler.asInstanceOf[SaveHandler].getWorldDirectory
return new File(DimensionManager.getCurrentSaveRootDirectory, world.provider.getSaveFolder)
}
def saveFile:File = new File(saveDir, "multipart.dat")
override def load()
{
try
{
val din = new DataInputStream(new FileInputStream(saveFile))
loadTag(CompressedStreamTools.readCompressed(din))
din.close()
}
catch
{
case e:Exception =>
}
loadTag(new NBTTagCompound)
}
def loadTag(tag:NBTTagCompound)
{
if(tag.hasKey("schedTime"))
schedTime = tag.getLong("schedTime")
else
schedTime = world.getTotalWorldTime
}
def saveTag:NBTTagCompound =
{
val tag = new NBTTagCompound
tag.setLong("schedTime", schedTime)
return tag
}
override def save() {
val file = saveFile
if(!file.getParentFile.exists)
file.getParentFile.mkdirs()
if(!file.exists)
file.createNewFile()
val dout = new DataOutputStream(new FileOutputStream(file))
CompressedStreamTools.writeCompressed(saveTag, dout)
dout.close()
}
def nextRandomTick = world.rand.nextInt(800)+800
}
def createWorldExtension(world:World):WorldExtension = new WorldTickScheduler(world)
private class ChunkTickScheduler(chunk$:Chunk, world:WorldTickScheduler) extends ChunkExtension(chunk$, world)
{
import codechicken.multipart.handler.MultipartProxy._
var tickList = ListBuffer[PartTickEntry]()
def schedTime = world.schedTime
def scheduleTick(part:TMultiPart, time:Long, random:Boolean)
{
val it = tickList.iterator
while(it.hasNext)
{
val e = it.next()
if(e.part == part)
{
if(e.random && !random)//only override an existing tick if we're going from random->scheduled
{
e.time = time
e.random = random
}
return
}
}
tickList+=new PartTickEntry(part, time, random)
if(tickList.size == 1)
world.tickChunks+=this
}
def nextRandomTick = world.nextRandomTick
def processTicks():Boolean =
{
tickList = tickList.filter(processTick)
return !tickList.isEmpty
}
def processTick(e:PartTickEntry):Boolean =
{
if(e.time <= schedTime)
{
if(e.part.tile != null)
{
if(e.random && e.part.isInstanceOf[IRandomUpdateTick])
e.part.asInstanceOf[IRandomUpdateTick].randomUpdate()
else
e.part.scheduledTick()
if(e.part.isInstanceOf[IRandomUpdateTick])
{
e.time = schedTime+nextRandomTick
e.random = true
return true
}
}
return false
}
return true
}
override def saveData(data:NBTTagCompound)
{
val tagList = new NBTTagList
tickList.foreach{e =>
val part = e.part
if(part.tile != null && !e.random)
{
val tag = new NBTTagCompound
tag.setShort("pos", indexInChunk(new BlockCoord(part.tile)).toShort)
tag.setByte("i", part.tile.partList.indexOf(part).toByte)
tag.setLong("time", e.time)
tagList.appendTag(tag)
}
}
if(tagList.tagCount > 0)
data.setTag("multipartTicks", tagList)
}
override def loadData(data:NBTTagCompound)
{
tickList.clear()
if(!data.hasKey("multipartTicks"))
return
val tagList = data.getTagList("multipartTicks", 10)
val cc = new ChunkCoordIntPair(0, 0)
for(i <- 0 until tagList.tagCount)
{
val tag = tagList.getCompoundTagAt(i)
val pos = indexInChunk(cc, tag.getShort("pos"))
val tile = chunk.chunkTileEntityMap.get(new ChunkPosition(pos.x, pos.y, pos.z))
if(tile.isInstanceOf[TileMultipart])
tickList+=new PartTickEntry(tile.asInstanceOf[TileMultipart].partList(tag.getByte("i")), tag.getLong("time"), false)
}
}
override def load()
{
val it = new ArrayList(chunk.chunkTileEntityMap.values).iterator
while(it.hasNext)
{
val t = it.next
if(t.isInstanceOf[TileMultipart])
{
val tmp = t.asInstanceOf[TileMultipart]
tmp.onChunkLoad()
tmp.partList.foreach(p =>
if(p.isInstanceOf[IRandomUpdateTick])
world.scheduleTick(p, nextRandomTick, true))
}
}
if(!tickList.isEmpty)
world.tickChunks+=this
}
override def unload()
{
if(!tickList.isEmpty)
world.tickChunks-=this
}
}
def createChunkExtension(chunk:Chunk, world:WorldExtension):ChunkExtension = new ChunkTickScheduler(chunk, world.asInstanceOf[WorldTickScheduler])
private[multipart] def loadRandomTick(part:TMultiPart)
{
getExtension(part.tile.getWorldObj).asInstanceOf[WorldTickScheduler].loadRandom(part)
}
/**
* Schedule a tick for part relative to the current time.
*/
def scheduleTick(part:TMultiPart, ticks:Int)
{
getExtension(part.tile.getWorldObj).asInstanceOf[WorldTickScheduler].scheduleTick(part, ticks, false)
}
/**
* Returns the current scheduler time. Like the world time, but unaffected by the time set command and other things changing time of day.
* Deprecated in favor of world.getTotalWorldTime
*/
@Deprecated
def getSchedulerTime(world:World):Long = getExtension(world).asInstanceOf[WorldTickScheduler].schedTime
}
-703
View File
@@ -1,703 +0,0 @@
package codechicken.multipart
import net.minecraft.tileentity.TileEntity
import scala.collection.mutable.ListBuffer
import codechicken.lib.packet.PacketCustom
import codechicken.lib.vec.{Cuboid6, BlockCoord, Vector3}
import net.minecraft.world.World
import java.util.List
import net.minecraft.nbt.NBTTagCompound
import codechicken.lib.data.MCDataOutput
import codechicken.multipart.handler.{MultipartCompatiblity, MultipartProxy, MultipartSPH}
import net.minecraft.block.Block
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagList
import java.util.Random
import codechicken.lib.lighting.LightMatrix
import net.minecraft.entity.item.EntityItem
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.entity.Entity
import scala.collection.JavaConversions._
import java.util.Collection
import codechicken.lib.raytracer.ExtendedMOP
import net.minecraft.util.Vec3
import java.lang.Iterable
import scala.collection.mutable.{Map => MMap}
import net.minecraft.util.AxisAlignedBB
class TileMultipart extends TileEntity
{
/**
* List of parts in this tile space
*/
var partList = Seq[TMultiPart]()
private var doesTick = false
private[multipart] def from(that:TileMultipart)
{
copyFrom(that)
loadFrom(that)
}
/**
* This method should be used for copying all the data from the fields in that container tile.
* This method will be automatically generated on java tile traits with fields if it is not overridden.
*/
def copyFrom(that:TileMultipart)
{
partList = that.partList
doesTick = that.doesTick
}
def loadFrom(that:TileMultipart)
{
partList.foreach(_.bind(this))
if(doesTick)
{
doesTick = false
setTicking(true)
}
}
/**
* Overidden in TSlottedTile when a part that goes in a slot is added
*/
def partMap(slot:Int):TMultiPart = null
/**
* Implicit java conversion of part list
*/
def jPartList():List[TMultiPart] = partList
override def canUpdate = doesTick
def operate(f:(TMultiPart)=>Unit) {
val it = partList.iterator
while(it.hasNext) {
val p = it.next()
if(p.tile != null) f(p)
}
}
override def updateEntity()
{
operate(_.update())
}
override def onChunkUnload()
{
operate(_.onChunkUnload())
}
def onChunkLoad()
{
operate(_.onChunkLoad())
}
final def setValid(b:Boolean)
{
if(b)
super.validate()
else
super.invalidate()
}
def onMoved()
{
operate(_.onMoved())
}
override def invalidate()
{
if(!isInvalid)
{
super.invalidate()
if(worldObj != null) {
partList.foreach(_.onWorldSeparate())
if(worldObj.isRemote)
TileMultipart.putClientCache(this)
}
}
}
/**
* Called by parts when they have changed in some form that affects the world.
* Notifies neighbor blocks, parts that share this host and recalculates lighting
*/
def notifyPartChange(part:TMultiPart)
{
internalPartChange(part)
worldObj.notifyBlocksOfNeighborChange(xCoord, yCoord, zCoord, MultipartProxy.block)
worldObj.func_147451_t(xCoord, yCoord, zCoord)
}
/**
* Notifies parts sharing this host of a change
*/
def internalPartChange(part:TMultiPart)
{
operate(p => if(part != p) p.onPartChanged(part))
}
/**
* Notifies all parts not in the passed collection of a change from all the parts in the collection
*/
def multiPartChange(parts:Collection[TMultiPart])
{
operate(p => if(!parts.contains(p)) parts.foreach(p.onPartChanged))
}
/**
* Notifies neighboring blocks that this tile has changed
*/
def notifyTileChange()
{
worldObj.func_147453_f(xCoord, yCoord, zCoord, MultipartProxy.block)
}
def onNeighborBlockChange()
{
operate(_.onNeighborChanged())
}
/**
* Blank implementation, overriden by TTileChangeTile
*/
def onNeighborTileChange(tileX:Int, tileY:Int, tileZ:Int) {}
/**
* Blank implementation, overriden by TTileChangeTile
*/
def getWeakChanges() = false
def getLightValue = partList.view.map(_.getLightValue).max
def getExplosionResistance(entity:Entity) = partList.view.map(_.explosionResistance(entity)).max
/**
* Callback for parts to mark the chunk as needs saving
*/
override def markDirty()
{
worldObj.markTileEntityChunkModified(xCoord, yCoord, zCoord, this)
}
/**
* Mark this block space for a render update.
*/
def markRender()
{
worldObj.func_147479_m(xCoord, yCoord, zCoord)
}
/**
* Helper function for calling a second level notify on a side (eg indirect power from a lever)
*/
def notifyNeighborChange(side:Int)
{
val pos = new BlockCoord(this).offset(side)
worldObj.notifyBlocksOfNeighborChange(pos.x, pos.y, pos.z, MultipartProxy.block)
}
def isSolid(side:Int):Boolean =
{
val part = partMap(side)
if(part != null)
return part.asInstanceOf[TFacePart].solid(side)
return false
}
private def setTicking(tick:Boolean)
{
if(doesTick == tick)
return
doesTick = tick
if(worldObj != null && worldObj.getTileEntity(xCoord, yCoord, zCoord) == this)
{
if(tick)
worldObj.addTileEntity(this)
else
worldObj.func_147457_a(this)
}
}
/**
* Returns true if part can be added to this space
*/
def canAddPart(part:TMultiPart) =
MultipartCompatiblity.canAddPart(worldObj, xCoord, yCoord, zCoord) &&
!partList.contains(part) &&
occlusionTest(partList, part)
/**
* Returns true if opart can be replaced with npart (note opart and npart may be the exact same object)
*
* This function should be used for testing if a part can change it's shape (eg. rotation, expansion, cable connection)
* For example, to test whether a cable part can connect to it's neighbor:
* 1. Set the cable part's bounding boxes as if the connection is established
* 2. Call canReplacePart(part, part)
* 3. If canReplacePart succeeds, perform connection, else, revert bounding box
*/
def canReplacePart(opart:TMultiPart, npart:TMultiPart):Boolean =
{
val olist = partList.filterNot(_ == opart)
if(olist.contains(npart))
return false
return occlusionTest(olist, npart)
}
/**
* Returns true if parts do not occlude npart
*/
def occlusionTest(parts:Seq[TMultiPart], npart:TMultiPart):Boolean =
{
return parts.forall(part => part.occlusionTest(npart) && npart.occlusionTest(part))
}
/**
* Get the write stream for updates to part
*/
def getWriteStream(part:TMultiPart):MCDataOutput = getWriteStream.writeByte(partList.indexOf(part))
private def getWriteStream = MultipartSPH.getTileStream(worldObj, new BlockCoord(this))
private[multipart] def addPart_impl(part:TMultiPart)
{
if(!worldObj.isRemote)
writeAddPart(part)
addPart_do(part)
part.onAdded()
partAdded(part)
notifyPartChange(part)
notifyTileChange()
markDirty()
markRender()
}
private[multipart] def writeAddPart(part:TMultiPart)
{
val stream = getWriteStream.writeByte(253)
MultiPartRegistry.writePartID(stream, part)
part.writeDesc(stream)
}
private[multipart] def addPart_do(part:TMultiPart)
{
assert(partList.size < 250, "Tried to add more than 250 parts to the one tile. You're doing it wrong")
partList = partList:+part
bindPart(part)
part.bind(this)
if(!doesTick && part.doesTick)
setTicking(true)
}
/**
* Bind this part to an internal cache.
* Provided for trait overrides, do not call externally.
*/
def bindPart(part:TMultiPart){}
/**
* Called when a part is added (placement)
* Provided for trait overrides, do not call externally.
*/
def partAdded(part:TMultiPart)
{
if(part.isInstanceOf[IRandomUpdateTick])
TickScheduler.loadRandomTick(part)
}
/**
* Removes part from this tile. Note that due to the operation sync, the part may not be removed until the call stack has been passed to all other parts in the space.
*/
def remPart(part:TMultiPart):TileMultipart =
{
assert(!worldObj.isRemote, "Cannot remove multi parts from a client tile")
remPart_impl(part)
}
private[multipart] def remPart_impl(part:TMultiPart):TileMultipart =
{
remPart_do(part, !worldObj.isRemote)
if(!isInvalid)
{
val tile = MultipartGenerator.partRemoved(this, part)
notifyPartChange(part)
markDirty()
markRender()
return tile
}
return null
}
private def remPart_do(part:TMultiPart, sendPacket:Boolean):Int =
{
val r = partList.indexOf(part)
if(r < 0)
throw new IllegalArgumentException("Tried to remove a non-existant part")
part.preRemove()
partList = partList.filterNot(_ == part)
if(sendPacket)
getWriteStream.writeByte(254).writeByte(r)
partRemoved(part, r)
part.onRemoved()
part.tile = null
if(partList.isEmpty)
{
worldObj.setBlockToAir(xCoord, yCoord, zCoord)
}
else
{
if(part.doesTick && doesTick)
{
var ntick = false
partList.foreach(part => ntick |= part.doesTick)
if(!ntick)
setTicking(false)
}
}
return r
}
/**
* Remove this part from internal cache.
* Provided for trait overrides, do not call externally.
*/
def partRemoved(part:TMultiPart, p:Int){}
private[multipart] def loadParts(parts:ListBuffer[TMultiPart])
{
clearParts()
parts.foreach(p => addPart_do(p))
if(worldObj != null) {
if(worldObj.isRemote)
operate(_.onWorldJoin())
notifyPartChange(null)
}
}
/**
* Remove all parts from internal cache
* Provided for trait overrides, do not call externally.
*/
def clearParts()
{
partList = Seq()
}
/**
* Writes the description of this tile, and all parts composing it, to packet
*/
def writeDesc(packet:MCDataOutput)
{
packet.writeByte(partList.size)
partList.foreach{part =>
MultiPartRegistry.writePartID(packet, part)
part.writeDesc(packet)
}
}
/**
* Perform a raytrace returning all intersecting parts sorted nearest to farthest
*/
def rayTraceAll(start:Vec3, end:Vec3):Iterable[ExtendedMOP] =
{
var list = ListBuffer[ExtendedMOP]()
for((p, i) <- partList.view.zipWithIndex)
p.collisionRayTrace(start, end) match {
case mop:ExtendedMOP =>
mop.data = (i, mop.data)
list+=mop
case _ =>
}
return list.sorted
}
/**
* Perform a raytrace returning the nearest intersecting part
*/
def collisionRayTrace(start:Vec3, end:Vec3):ExtendedMOP = rayTraceAll(start, end).headOption.getOrElse(null)
/**
* Drop and remove part at index (internal mining callback)
*/
def harvestPart(index:Int, hit:ExtendedMOP, player:EntityPlayer) = partList(index) match {
case null =>
case part => part.harvest(hit, player)
}
/**
* Utility function for dropping items around the center of this space
*/
def dropItems(items:Iterable[ItemStack])
{
val pos = Vector3.fromTileEntityCenter(this)
items.foreach(item => TileMultipart.dropItem(item, worldObj, pos))
}
final override def writeToNBT(tag:NBTTagCompound)
{
super.writeToNBT(tag)
val taglist = new NBTTagList
partList.foreach{part =>
val parttag = new NBTTagCompound
parttag.setString("id", part.getType)
part.save(parttag)
taglist.appendTag(parttag)
}
tag.setTag("parts", taglist)
}
/**
* Internal callback
*/
def onEntityCollision(entity:Entity)
{
operate(_.onEntityCollision(entity))
}
/**
* Internal callback, overriden in TRedstoneTile
*/
def strongPowerLevel(side:Int) = 0
/**
* Internal callback, overriden in TRedstoneTile
*/
def weakPowerLevel(side:Int) = 0
/**
* Internal callback, overriden in TRedstoneTile
*/
def canConnectRedstone(side:Int) = false
}
class TileMultipartClient extends TileMultipart
{
def renderStatic(pos:Vector3, pass:Int) =
partList.foldLeft(false)((r, part) => part.renderStatic(pos, pass) || r)
def renderDynamic(pos:Vector3, frame:Float, pass:Int)
{
partList.foreach(part => part.renderDynamic(pos, frame, pass:Int))
}
def randomDisplayTick(random:Random){}
override def shouldRenderInPass(pass:Int) =
{
MultipartRenderer.pass = pass
true
}
override def getRenderBoundingBox = {
val c = Cuboid6.full.copy
partList.foreach(part => c.enclose(part.getRenderBounds))
c.add(Vector3.fromTileEntity(this)).toAABB
}
}
/**
* Static class with multipart manipulation helper functions
*/
object TileMultipart
{
var renderID = -1
/**
* Playerinstance will often remove the tile entity instance and set the block to air on the client before the multipart packet handler fires it's updates.
* In order to maintain the packet data, and make sure all written data is read, the tile needs to be kept around internally until
*/
private val clientFlushMap = MMap[BlockCoord, TileMultipart]()
private[multipart] def flushClientCache() = clientFlushMap.clear()
private[multipart] def putClientCache(t:TileMultipart) = clientFlushMap.put(new BlockCoord(t), t)
/**
* Gets a multipar ttile instance at pos, converting if necessary.
*/
def getOrConvertTile(world:World, pos:BlockCoord) = getOrConvertTile2(world, pos)._1
/**
* Gets a multipart tile instance at pos, converting if necessary.
* Note converted tiles are merely a structure formality,
* they do not actually exist in world until they are required to by the addition of another multipart to their space.
* @return (The tile or null if there was none, true if the tile is a result of a conversion)
*/
def getOrConvertTile2(world:World, pos:BlockCoord):(TileMultipart, Boolean) =
{
val t = world.getTileEntity(pos.x, pos.y, pos.z)
if(t.isInstanceOf[TileMultipart])
return (t.asInstanceOf[TileMultipart], false)
val p = MultiPartRegistry.convertBlock(world, pos, world.getBlock(pos.x, pos.y, pos.z))
if(p != null)
{
val t = MultipartGenerator.generateCompositeTile(null, Seq(p), world.isRemote)
t.xCoord = pos.x
t.yCoord = pos.y
t.zCoord = pos.z
t.setWorldObj(world)
t.addPart_do(p)
return (t, true)
}
return (null, false)
}
/**
* Gets the multipart tile instance at pos, or null if it doesn't exist or is not a multipart tile
*/
def getTile(world:World, pos:BlockCoord) =
world.getTileEntity(pos.x, pos.y, pos.z) match {
case t:TileMultipart => t
case _ => null
}
def checkNoEntityCollision(world:World, pos:BlockCoord, part:TMultiPart) =
part.getCollisionBoxes.forall(b => world.checkNoEntityCollision(b.toAABB.offset(pos.x, pos.y, pos.z)))
/**
* Returns whether part can be added to the space at pos. Will do conversions as necessary.
* This function is the recommended way to add parts to the world.
*/
def canPlacePart(world:World, pos:BlockCoord, part:TMultiPart):Boolean =
{
if(!checkNoEntityCollision(world, pos, part))
return false
val t = getOrConvertTile(world, pos)
if(t != null)
return t.canAddPart(part)
else if(!MultipartCompatiblity.canAddPart(world, pos.x, pos.y, pos.z))
return false
if(!replaceable(world, pos))
return false
return true
}
/**
* Returns if the block at pos is replaceable (air, vines etc)
*/
def replaceable(world:World, pos:BlockCoord):Boolean =
{
val block = world.getBlock(pos.x, pos.y, pos.z)
return block.isAir(world, pos.x, pos.y, pos.z) || block.isReplaceable(world, pos.x, pos.y, pos.z)
}
/**
* Adds a part to a block space. canPlacePart should always be called first.
* The addition of parts on the client is handled internally.
*/
def addPart(world:World, pos:BlockCoord, part:TMultiPart):TileMultipart =
{
assert(!world.isRemote, "Cannot add multi parts to a client tile.")
return MultipartGenerator.addPart(world, pos, part)
}
/**
* Constructs this tile and its parts from a desc packet
*/
def handleDescPacket(world:World, pos:BlockCoord, packet:PacketCustom)
{
val nparts = packet.readUByte
val parts = new ListBuffer[TMultiPart]()
for(i <- 0 until nparts)
{
val part:TMultiPart = MultiPartRegistry.readPart(packet)
part.readDesc(packet)
parts+=part
}
if(parts.size == 0)
return
val t = world.getTileEntity(pos.x, pos.y, pos.z)
val tilemp = MultipartGenerator.generateCompositeTile(t, parts, true)
if(tilemp != t) {
world.setBlock(pos.x, pos.y, pos.z, MultipartProxy.block)
MultipartGenerator.silentAddTile(world, pos, tilemp)
}
tilemp.loadParts(parts)
tilemp.notifyTileChange()
tilemp.markRender()
}
/**
* Handles an update packet, addition, removal and otherwise
*/
def handlePacket(pos:BlockCoord, world:World, i:Int, packet:PacketCustom)
{
lazy val tilemp = Option(BlockMultipart.getTile(world, pos.x, pos.y, pos.z)).getOrElse(clientFlushMap(pos))
i match
{
case 253 =>
val part = MultiPartRegistry.readPart(packet)
part.readDesc(packet)
MultipartGenerator.addPart(world, pos, part)
case 254 => tilemp.remPart_impl(tilemp.partList(packet.readUByte))
case _ => tilemp.partList(i).read(packet)
}
}
/**
* Creates this tile from an NBT tag
*/
def createFromNBT(tag:NBTTagCompound):TileMultipart =
{
val partList = tag.getTagList("parts", 10)
val parts = ListBuffer[TMultiPart]()
for(i <- 0 until partList.tagCount)
{
val partTag = partList.getCompoundTagAt(i)
val partID = partTag.getString("id")
val part = MultiPartRegistry.createPart(partID, false)
if(part != null)
{
part.load(partTag)
parts+=part
}
}
if(parts.size == 0)
return null
val tmb = MultipartGenerator.generateCompositeTile(null, parts, false)
tmb.readFromNBT(tag)
tmb.loadParts(parts)
return tmb
}
/**
* Drops an item around pos
*/
def dropItem(stack:ItemStack, world:World, pos:Vector3)
{
val item = new EntityItem(world, pos.x, pos.y, pos.z, stack)
item.motionX = world.rand.nextGaussian() * 0.05
item.motionY = world.rand.nextGaussian() * 0.05 + 0.2
item.motionZ = world.rand.nextGaussian() * 0.05
item.delayBeforeCanPickup = 10
world.spawnEntityInWorld(item)
}
}
@@ -1,627 +0,0 @@
package codechicken.multipart.asm
import scala.collection.mutable.{Map => MMap, ListBuffer => MList, Set => MSet}
import java.util.{Set => JSet}
import scala.collection.JavaConversions._
import org.objectweb.asm.tree._
import org.objectweb.asm.Opcodes._
import org.objectweb.asm.ClassReader
import org.objectweb.asm.util.TraceClassVisitor
import org.objectweb.asm.util.Textifier
import org.objectweb.asm.Type
import org.objectweb.asm.MethodVisitor
import Type._
import codechicken.lib.asm.ASMHelper._
import codechicken.lib.asm.ObfMapping
import java.io.File
import java.io.PrintWriter
import ScalaSignature._
import java.lang.reflect.Method
import java.lang.reflect.Modifier
import net.minecraft.launchwrapper.LaunchClassLoader
import codechicken.multipart.handler.MultipartProxy
import cpw.mods.fml.common.asm.transformers.deobf.FMLDeobfuscatingRemapper
object DebugPrinter
{
val debug = MultipartProxy.config.getTag("debug_asm").getBooleanValue(!ObfMapping.obfuscated)
private var permGenUsed = 0
val dir = new File("asm/multipart")
if(debug)
{
if(!dir.exists)
dir.mkdirs()
for(file <- dir.listFiles)
file.delete
}
def dump(name:String, bytes:Array[Byte])
{
if(!debug) return
val fileout = new File(dir, name.replace('/', '#')+".txt")
val pout = new PrintWriter(fileout)
new ClassReader(bytes).accept(new TraceClassVisitor(null, new Textifier(), pout), 0)
pout.close()
}
def defined(name:String, bytes:Array[Byte])
{
if((permGenUsed+bytes.length)/16000 != permGenUsed/16000)
log((permGenUsed+bytes.length)+" bytes of permGen has been used by ASMMixinCompiler")
permGenUsed += bytes.length
}
def log(msg:String) = if(debug) println(msg)
}
object ASMMixinCompiler
{
import StackAnalyser.width
val cl = getClass.getClassLoader.asInstanceOf[LaunchClassLoader]
val m_defineClass = classOf[ClassLoader].getDeclaredMethod("defineClass", classOf[Array[Byte]], Integer.TYPE, Integer.TYPE)
val m_runTransformers = classOf[LaunchClassLoader].getDeclaredMethod("runTransformers", classOf[String], classOf[String], classOf[Array[Byte]])
val f_transformerExceptions = classOf[LaunchClassLoader].getDeclaredField("transformerExceptions")
m_defineClass.setAccessible(true)
m_runTransformers.setAccessible(true)
f_transformerExceptions.setAccessible(true)
private val traitByteMap = MMap[String, Array[Byte]]()
private val mixinMap = MMap[String, MixinInfo]()
def define(name:String, bytes:Array[Byte]) =
{
internalDefine(name, bytes)
DebugPrinter.defined(name, bytes)
try {
m_defineClass.invoke(cl, bytes, 0:Integer, bytes.length:Integer).asInstanceOf[Class[_]]
} catch {
case link:LinkageError if link.getMessage.contains("duplicate") =>
throw new IllegalStateException("class with name: "+name+" already loaded. Do not reference your mixin classes before registering them with MultipartGenerator", link)
}
}
getBytes("cpw/mods/fml/common/asm/FMLSanityChecker")
def getBytes(name:String):Array[Byte] =
{
val jName = name.replace('/', '.')
if(jName.equals("java.lang.Object"))
return null
def useTransformers = f_transformerExceptions.get(cl).asInstanceOf[JSet[String]]
.find(jName.startsWith).isEmpty
val obfName = if(ObfMapping.obfuscated) FMLDeobfuscatingRemapper.INSTANCE.unmap(name).replace('/', '.') else jName
val bytes = cl.getClassBytes(obfName)
if(bytes != null && useTransformers)
return m_runTransformers.invoke(cl, jName, obfName, bytes).asInstanceOf[Array[Byte]]
return bytes
}
def internalDefine(name:String, bytes:Array[Byte])
{
traitByteMap.put(name.replace('.', '/'), bytes)
BaseNodeInfo.clear(name)
DebugPrinter.dump(name, bytes)
}
def classNode(name:String) = traitByteMap.getOrElseUpdate(name.replace('.', '/'), getBytes(name.replace('.', '/'))) match {
case null => null
case v => createClassNode(v, ClassReader.EXPAND_FRAMES)
}
def isScala(cnode:ClassNode) = ScalaSigReader.ann(cnode).isDefined
def isTrait(cnode:ClassNode) =
{
val csym:ClassSymbol = ScalaSigReader.read(ScalaSigReader.ann(cnode).get).evalT(0)
csym.isTrait && !csym.isInterface
}
def getMixinInfo(name:String) = mixinMap.get(name)
case class FieldMixin(name:String, desc:String, access:Int)
{
def accessName(owner:String) = if((access & ACC_PRIVATE) != 0)
owner.replace('/', '$')+"$$"+name
else
name
}
case class MixinInfo(name:String, parent:String, fields:Seq[FieldMixin],
methods:Seq[MethodNode], supers:Map[String, String])
{
def tname = name+"$class"
}
object BaseNodeInfo
{
private val baseNodeMap = MMap[String, BaseNodeInfo]()
def clear(name:String) = baseNodeMap.remove(name)
def getNodeInfo(name:String) = baseNodeMap.getOrElseUpdate(name, new BaseNodeInfo(name))
private def getNodeInfo(clazz:ClassInfoSource) = clazz match {
case null => null
case v => baseNodeMap.getOrElseUpdate(v.name, new BaseNodeInfo(v))
}
case class MethodNodeInfo(owner:String, m:MethodInfoSource)
{
def name = m.name
def desc = m.desc
}
trait MethodInfoSource
{
def name:String
def desc:String
def isPrivate:Boolean
def isAbstract:Boolean
}
trait ClassInfoSource
{
def name:String
def superClass:Option[BaseNodeInfo]
def interfaces:Seq[BaseNodeInfo]
def methods:Seq[MethodInfoSource]
}
implicit def JClassInfoSource(clazz:Class[_]) = if(clazz == null) null else new JClassInfoSource(clazz)
class JClassInfoSource(clazz:Class[_]) extends ClassInfoSource
{
case class JMethodInfoSource(method:Method) extends MethodInfoSource
{
def name = method.getName
def desc = getType(method).getDescriptor
def isPrivate = Modifier.isPrivate(method.getModifiers)
def isAbstract = Modifier.isAbstract(method.getModifiers)
}
def name = clazz.getName.replace('.', '/')
def superClass = Option(getNodeInfo(clazz.getSuperclass))
def interfaces = clazz.getInterfaces.map(getNodeInfo(_))
def methods = clazz.getMethods.map(JMethodInfoSource(_))
}
implicit def ClassNodeInfoSource(cnode:ClassNode) = if(cnode == null) null else new ClassNodeInfoSource(cnode)
class ClassNodeInfoSource(cnode:ClassNode) extends ClassInfoSource
{
case class MethodNodeInfoSource(mnode:MethodNode) extends MethodInfoSource
{
def name = mnode.name
def desc = mnode.desc
def isPrivate = (mnode.access & ACC_PRIVATE) != 0
def isAbstract = (mnode.access & ACC_ABSTRACT) != 0
}
def name = cnode.name
def superClass = Option(getNodeInfo(cnode.superName))
def interfaces = cnode.interfaces match {
case null => Seq()
case v => v.map(getNodeInfo)
}
def methods = cnode.methods.map(MethodNodeInfoSource(_))
}
class ScalaNodeInfoSource(cnode:ClassNode) extends ClassNodeInfoSource(cnode)
{
val sig = ScalaSigReader.read(ScalaSigReader.ann(cnode).get)
val csym = sig.evalT(0):ClassSymbol
override def superClass = Option(getNodeInfo(csym.jParent(sig)))
override def interfaces = csym.jInterfaces(sig).map(getNodeInfo)
}
def classInfo(name:String):ClassInfoSource = name match {
case null => null
case s => classNode(s) match {
case null => cl.findClass(s.replace('/', '.'))
case v if isScala(v) => new ScalaNodeInfoSource(v)
case v => v
}
}
}
import BaseNodeInfo._
class BaseNodeInfo(val clazz:ClassInfoSource)
{
def this(name:String) = this(classInfo(name))
val publicmethods = exportedMethods(clazz)//map of nameDesc to owner+method
def exportedMethods(o:ClassInfoSource):Map[String, MethodNodeInfo] =
{
val eMethods = o.methods.filter(!_.isPrivate)//defined accessible methods
.map(m => (m.name+m.desc, MethodNodeInfo(o.name, m))).toMap
eMethods ++ (o.superClass++o.interfaces).flatMap(_.publicmethods)
}
}
def finishBridgeCall(mv:MethodVisitor, mvdesc:String, opcode:Int, owner:String, name:String, desc:String)
{
val args = getArgumentTypes(mvdesc)
val ret = getReturnType(mvdesc)
var localIndex = 1
args.foreach{arg =>
mv.visitVarInsn(arg.getOpcode(ILOAD), localIndex)
localIndex+=width(arg)
}
mv.visitMethodInsn(opcode, owner, name, desc)
mv.visitInsn(ret.getOpcode(IRETURN))
mv.visitMaxs(Math.max(width(args)+1, width(ret)), width(args)+1)
}
def writeBridge(mv:MethodVisitor, mvdesc:String, opcode:Int, owner:String, name:String, desc:String)
{
mv.visitVarInsn(ALOAD, 0)
finishBridgeCall(mv, mvdesc, opcode, owner, name, desc)
}
def writeStaticBridge(mv:MethodNode, mname:String, t:MixinInfo) =
writeBridge(mv, mv.desc, INVOKESTATIC, t.tname, mname, staticDesc(t.name, mv.desc))
def mixinClasses(name:String, superClass:String, traits:Seq[String]) =
{
val traitInfos = traits.map(t => mixinMap(t.replace('.', '/')))
val cnode = new ClassNode()
cnode.visit(V1_6, ACC_PUBLIC, name, null, superClass.replace('.', '/'), traitInfos.map(_.name).toArray[String])
val minit = cnode.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null)
minit.visitVarInsn(ALOAD, 0)
minit.visitMethodInsn(INVOKESPECIAL, cnode.superName, "<init>", "()V")
val prevInfos = MList[MixinInfo]()
traitInfos.foreach{ t =>
minit.visitVarInsn(ALOAD, 0)
minit.visitMethodInsn(INVOKESTATIC, t.tname, "$init$", "(L"+t.name+";)V")
t.fields.foreach{ f =>
val fv = cnode.visitField(ACC_PRIVATE, f.accessName(t.name), f.desc, null, null).asInstanceOf[FieldNode]
val ftype = getType(fv.desc)
var mv = cnode.visitMethod(ACC_PUBLIC, fv.name, "()"+f.desc, null, null)
mv.visitVarInsn(ALOAD, 0)
mv.visitFieldInsn(GETFIELD, name, fv.name, fv.desc)
mv.visitInsn(ftype.getOpcode(IRETURN))
mv.visitMaxs(1, 1)
mv = cnode.visitMethod(ACC_PUBLIC, fv.name+"_$eq", "("+f.desc+")V", null, null)
mv.visitVarInsn(ALOAD, 0)
mv.visitVarInsn(ftype.getOpcode(ILOAD), 1)
mv.visitFieldInsn(PUTFIELD, name, fv.name, fv.desc)
mv.visitInsn(RETURN)
mv.visitMaxs(width(ftype)+1, width(ftype)+1)
}
t.supers.foreach{ s =>
val (name, desc) = seperateDesc(s._1)
val mv = cnode.visitMethod(ACC_PUBLIC, t.name.replace('/', '$')+"$$super$"+name, desc, null, null).asInstanceOf[MethodNode]
prevInfos.reverse.find(t => t.supers.contains(s._1)) match {//each super goes to the one before
case Some(st) => writeStaticBridge(mv, name, st)
case None => writeBridge(mv, desc, INVOKESPECIAL, s._2, name, desc)
}
}
prevInfos+=t
}
val methodSigs = MSet[String]()
traitInfos.reverse.foreach{ t => //last trait gets first pick on methods
t.methods.foreach{ m =>
if(!methodSigs(m.name+m.desc))
{
val mv = cnode.visitMethod(ACC_PUBLIC, m.name, m.desc, null, Array(m.exceptions:_*)).asInstanceOf[MethodNode]
copy(m, mv)
mv.instructions = new InsnList()
mv.tryCatchBlocks.clear()
writeStaticBridge(mv, m.name, t)
methodSigs+=m.name+m.desc
}
}
}
minit.visitInsn(RETURN)
minit.visitMaxs(1, 1)
define(cnode.name, createBytes(cnode, 0))
}
def seperateDesc(nameDesc:String) = {
val n = nameDesc.indexOf('(')
(nameDesc.substring(0, n), nameDesc.substring(n))
}
def staticDesc(owner:String, desc:String) =
{
val descT = getMethodType(desc)
getMethodDescriptor(descT.getReturnType, getType("L"+owner+";")+:descT.getArgumentTypes : _*)
}
def getSuper(minsn:MethodInsnNode, stack:StackAnalyser):Option[String] =
{
import StackAnalyser._
if(minsn.getOpcode != INVOKESPECIAL) return None
val oname = stack.owner.getInternalName
if(minsn.owner.equals(oname)) return None//private this
stack.peek(Type.getType(minsn.desc).getArgumentTypes.length) match {
case Load(This(o)) =>
case _ => return None//have to be invoked on this
}
getSuper(minsn.name+minsn.desc, oname)
}
def getSuper(nameDesc:String, cname:String) =
getNodeInfo(cname).clazz.superClass match {
case None => None//no super class
case Some(o) => o.publicmethods.get(nameDesc) match {
case None => None//no method with sig in super
case Some(m) => if(m.m.isAbstract)
None //abstract can't be called for supers
else
Some(m.owner)//congrats, that's a super
}
}
def registerJavaTrait(cnode:ClassNode)
{
if((cnode.access & ACC_INTERFACE) != 0)
throw new IllegalArgumentException("Cannot register java interface "+cnode.name+" as a multipart trait. Try register passThroughInterface")
if((cnode.access & ACC_ABSTRACT) != 0)
throw new IllegalArgumentException("Cannot register abstract class "+cnode.name+" as a multipart trait")
if(!cnode.innerClasses.isEmpty)
throw new IllegalArgumentException("Inner classes are not permitted for "+cnode.name+" as a multipart trait. Use scala")
val inode = new ClassNode()//impl node
inode.visit(V1_6, ACC_ABSTRACT|ACC_PUBLIC, cnode.name+"$class", null, "java/lang/Object", null)
inode.sourceFile = cnode.sourceFile
val fields = cnode.fields.map(f => (f.name, FieldMixin(f.name, f.desc, f.access))).toMap
val supers = MMap[String, String]()//nameDesc to super owner
val methods = MList[MethodNode]()
val methodSigs = cnode.methods.map(m => m.name+m.desc).toSet
val tnode = new ClassNode()//trait node (interface)
tnode.visit(V1_6, ACC_INTERFACE|ACC_ABSTRACT|ACC_PUBLIC, cnode.name, null, "java/lang/Object", Array(cnode.interfaces:_*))
def fname(name:String) = fields(name).accessName(cnode.name)
fields.values.foreach{ fnode =>
tnode.visitMethod(ACC_PUBLIC|ACC_ABSTRACT, fname(fnode.name), "()"+fnode.desc, null, null)
tnode.visitMethod(ACC_PUBLIC|ACC_ABSTRACT, fname(fnode.name)+"_$eq", "("+fnode.desc+")V", null, null)
}
def superInsn(minsn:MethodInsnNode) =
{
val bridgeName = cnode.name.replace('/', '$')+"$$super$"+minsn.name
if(!supers.contains(minsn.name+minsn.desc))
{
tnode.visitMethod(ACC_PUBLIC|ACC_ABSTRACT, bridgeName, minsn.desc, null, null)
supers.put(minsn.name+minsn.desc, minsn.owner)
}
new MethodInsnNode(INVOKEINTERFACE, cnode.name, bridgeName, minsn.desc)
}
def staticClone(mnode:MethodNode, name:String, access:Int) =
{
val mv = inode.visitMethod(access|ACC_STATIC, name,
staticDesc(cnode.name, mnode.desc),
null, Array(mnode.exceptions:_*)).asInstanceOf[MethodNode]
copy(mnode, mv)
mv
}
def staticTransform(mnode:MethodNode, base:MethodNode)
{
val stack = new StackAnalyser(getType(cnode.name), base)
val insnList = mnode.instructions
var insn = insnList.getFirst
def replace(newinsn:AbstractInsnNode)
{
insnList.insert(insn, newinsn)
insnList.remove(insn)
insn = newinsn
}
//transform
while(insn != null)
{
insn match {
case finsn:FieldInsnNode => insn.getOpcode match
{
case GETFIELD => replace(new MethodInsnNode(INVOKEINTERFACE, cnode.name,
fname(finsn.name), "()"+finsn.desc))
case PUTFIELD => replace(new MethodInsnNode(INVOKEINTERFACE, cnode.name,
fname(finsn.name)+"_$eq", "("+finsn.desc+")V"))
case _ =>
}
case minsn:MethodInsnNode => insn.getOpcode match
{
case INVOKESPECIAL =>
if(getSuper(minsn, stack).isDefined)
replace(superInsn(minsn))
case INVOKEVIRTUAL =>
if(minsn.owner.equals(cnode.name)) {
if(methodSigs.contains(minsn.name+minsn.desc))//call the interface method
minsn.setOpcode(INVOKEINTERFACE)
else {//cast to parent class and call
val mType = Type.getMethodType(minsn.desc)
val instanceEntry = stack.peek(width(mType.getArgumentTypes))
insnList.insert(instanceEntry.insn, new TypeInsnNode(CHECKCAST, cnode.superName))
minsn.owner = cnode.superName
}
}
case _ =>
}
case _ =>
}
stack.visitInsn(insn)
insn = insn.getNext
}
}
def convertMethod(mnode:MethodNode)
{
if(mnode.name.equals("<clinit>"))
throw new IllegalArgumentException("Static initialisers are not permitted "+cnode.name+" as a multipart trait")
if(mnode.name.equals("<init>"))
{
if(!mnode.desc.equals("()V"))
throw new IllegalArgumentException("Constructor arguments are not permitted "+cnode.name+" as a multipart trait")
val mv = staticClone(mnode, "$init$", ACC_PUBLIC)
def removeSuperConstructor()
{
def throwI = throw new IllegalArgumentException("Invalid constructor insn sequence "+cnode.name)
val insnList = mv.instructions
var insn = insnList.getFirst
var state = 0
while(insn != null && state < 2)
{
val next = insn.getNext
insn match {
case linsn:LabelNode =>
case linsn:LineNumberNode =>
case vinsn:VarInsnNode =>
if(state == 0)
{
if(vinsn.getOpcode == ALOAD && vinsn.`var` == 0)
{
state = 1
insnList.remove(insn)
}
else
throwI
}
else
throwI
case minsn:MethodInsnNode =>
if(state == 1)
{
if(minsn.getOpcode == INVOKESPECIAL && minsn.name.equals("<init>") &&
minsn.owner.equals(cnode.superName))
{
insnList.remove(insn)
state = 2
}
else
throwI
}
else
throwI
case _ => throwI
}
insn = next
}
if(state != 2)
throwI
}
removeSuperConstructor()
staticTransform(mv, mnode)
return
}
if((mnode.access & ACC_PRIVATE) == 0)
{
val mv = tnode.visitMethod(ACC_PUBLIC|ACC_ABSTRACT, mnode.name, mnode.desc, null, Array(mnode.exceptions:_*))
methods+=mv.asInstanceOf[MethodNode]
}
//convert that method!
val access = if((mnode.access & ACC_PRIVATE) == 0) ACC_PUBLIC else ACC_PRIVATE
val mv = staticClone(mnode, mnode.name, access)
staticTransform(mv, mnode)
}
cnode.methods.foreach(convertMethod)
define(inode.name, createBytes(inode, 0))
define(tnode.name, createBytes(tnode, 0))
mixinMap.put(tnode.name, MixinInfo(tnode.name, cnode.superName,
fields.values.toSeq,
methods.toSeq, supers.toMap))
}
def registerScalaTrait(cnode:ClassNode)
{
for(i <- cnode.interfaces)
{
val inode = classNode(i)
if(isScala(inode) && isTrait(inode))
throw new IllegalArgumentException("Multipart trait "+cnode.name+" cannot extend other traits")
}
val fieldAccessors = MMap[String, MethodSymbol]()
val fields = MList[MethodSymbol]()
val methods = MList[MethodNode]()
val supers = MMap[String, String]()
val sig = ScalaSigReader.read(ScalaSigReader.ann(cnode).get)
val csym:ClassSymbol = sig.evalT(0)
for(i <- 0 until sig.table.length)
{
import ScalaSignature._
val e = sig.table(i)
if(e.id == 8)//method
{
val sym:MethodSymbol = sig.evalT(i)
if(sym.isParam || !sym.owner.equals(csym)){}
else if(sym.isAccessor)
{
fieldAccessors.put(sym.name, sym)
}
else if(sym.isMethod)
{
val desc = sym.jDesc(sig)
if(sym.name.contains("super$"))
{
val name = sym.name.substring(6)
supers.put(name+desc, getSuper(name+desc, cnode.name).get)
}
else if(!sym.name.equals("$init$") && !sym.isPrivate)
{
val map = new ObfMapping(cnode.name, sym.name, desc)
val method = findMethod(map, cnode)
if(method == null)
throw new RuntimeException("Unable to add multipart trait: "+map+" found in scala signature but not in class file. Most likely an obfuscation issue.")
methods+=method
}
}
else
{
fields+=sym
}
}
}
mixinMap.put(cnode.name, MixinInfo(cnode.name, csym.jParent(sig),
fields.map(sym => FieldMixin(sym.name.trim, getReturnType(sym.jDesc(sig)).getDescriptor,
if(fieldAccessors(sym.name.trim).isPrivate) ACC_PRIVATE else ACC_PUBLIC)),
methods, supers.toMap))
}
}
@@ -1,311 +0,0 @@
package codechicken.multipart.asm
import scala.collection.Seq
import codechicken.multipart.TileMultipart
import codechicken.multipart.TileMultipartClient
import scala.collection.JavaConversions._
import org.objectweb.asm.Opcodes._
import codechicken.lib.asm.CC_ClassWriter
import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.FieldVisitor
import org.objectweb.asm.Label
import org.objectweb.asm.tree._
import codechicken.lib.asm.ASMHelper._
import codechicken.lib.asm.ObfMapping
import codechicken.multipart.MultipartGenerator
import ASMMixinCompiler._
object ASMMixinFactory extends IMultipartFactory
{
type TMClass = Class[_ <: TileMultipart]
private var ugenid = 0
private var generatorMap = Map[SuperSet, Constructor]()
def uniqueName(prefix:String):String = {
val ret = prefix+"$$"+ugenid
ugenid += 1
return ret
}
def simpleName(name:String) = name.substring(name.replace('/', '.').lastIndexOf('.')+1)
abstract class Constructor
{
def generate():TileMultipart
}
object SuperSet
{
val TileMultipartType = classOf[TileMultipart].getName
val TileMultipartClientType = classOf[TileMultipartClient].getName
def apply(types:Seq[String], client:Boolean) = new SuperSet(types, client)
}
class SuperSet(types:Seq[String], client:Boolean)
{
import SuperSet._
val set = baseType+:types.sorted
def baseType = if(client) TileMultipartClientType else TileMultipartType
override def equals(obj:Any) = obj match
{
case x:SuperSet => set == x.set
case _ => false
}
override def hashCode = set.hashCode()
def generate:TileMultipart = get.generate()
def get = generatorMap.getOrElse(this, gen_sync())
def gen_sync():Constructor = ASMMixinFactory.synchronized
{
return generatorMap.getOrElse(this, {
val gen = generator()
generatorMap = generatorMap+(this->gen)
gen
})
}
def generator():Constructor =
{
val startTime = System.currentTimeMillis
val cmpClass = if(!types.isEmpty)
mixinClasses(uniqueName("TileMultipart_cmp"), set.head, set.drop(1))
else
cl.loadClass(baseType)
MultipartGenerator.registerTileClass(cmpClass.asInstanceOf[TMClass], types.toSet)
val c = constructor(cmpClass)
DebugPrinter.log("Generation ["+types.mkString(", ")+"] took: "+(System.currentTimeMillis-startTime))
return c
}
}
def constructor(clazz:Class[_]):Constructor =
{
val name = uniqueName("TileMultipart_gen")
val cw = new CC_ClassWriter(ASM4)
val superName = classOf[Constructor].getName.replace('.', '/')
val className = clazz.getName.replace('.', '/')
var mv:MethodVisitor = null
cw.visit(V1_6, ACC_PUBLIC|ACC_SUPER, name, null, superName, null)
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null)
mv.visitCode()
mv.visitVarInsn(ALOAD, 0)
mv.visitMethodInsn(INVOKESPECIAL, superName, "<init>", "()V")
mv.visitInsn(RETURN)
mv.visitMaxs(1, 1)
mv.visitEnd()
mv = cw.visitMethod(ACC_PUBLIC, "generate", "()Lcodechicken/multipart/TileMultipart;", null, null)
mv.visitCode()
mv.visitTypeInsn(NEW, className)
mv.visitInsn(DUP)
mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", "()V")
mv.visitInsn(ARETURN)
mv.visitMaxs(2, 1)
mv.visitEnd()
cw.visitEnd()
return define(name, cw.toByteArray).newInstance.asInstanceOf[Constructor]
}
private def autoCompleteJavaTrait(cnode:ClassNode)
{
if(!cnode.fields.isEmpty && findMethod(new ObfMapping(cnode.name, "copyFrom", "(Lcodechicken/multipart/TileMultipart;)V"), cnode) == null)
{
val mv = cnode.visitMethod(ACC_PUBLIC, "copyFrom", "(Lcodechicken/multipart/TileMultipart;)V", null, null)
mv.visitVarInsn(ALOAD, 0)
mv.visitVarInsn(ALOAD, 1)
mv.visitMethodInsn(INVOKESPECIAL, "codechicken/multipart/TileMultipart", "copyFrom", "(Lcodechicken/multipart/TileMultipart;)V")
mv.visitVarInsn(ALOAD, 1)
mv.visitTypeInsn(INSTANCEOF, cnode.name)
val end = new Label()
mv.visitJumpInsn(IFEQ, end)
cnode.fields.foreach{ f =>
mv.visitVarInsn(ALOAD, 0)
mv.visitVarInsn(ALOAD, 1)
mv.visitFieldInsn(GETFIELD, cnode.name, f.name, f.desc)
mv.visitFieldInsn(PUTFIELD, cnode.name, f.name, f.desc)
}
mv.visitLabel(end)
mv.visitInsn(RETURN)
mv.visitMaxs(2, 2)
}
}
def registerTrait(s_interface:String, s_trait:String, client:Boolean)
{
val cnode = classNode(s_trait)
if(cnode == null)
throw new ClassNotFoundException(s_trait)
def superClass = getMixinInfo(cnode.name) match {
case Some(m) => m.parent
case None => BaseNodeInfo.getNodeInfo(cnode.name).clazz.superClass.get.clazz.name
}
superClass match {
case "codechicken/multipart/TileMultipartClient" => if(!client)
throw new IllegalArgumentException("Multipart trait "+s_trait+" cannot implement TileMultipartClient on the server")
case "codechicken/multipart/TileMultipart" =>
case _ => throw new IllegalArgumentException("Multipart trait "+s_trait+" must implement TileMultipart or TileMultipartClient")
}
if(getMixinInfo(cnode.name).isDefined)
return
if(isScala(cnode) && isTrait(cnode))
{
registerScalaTrait(cnode)
}
else
{
autoCompleteJavaTrait(cnode)
registerJavaTrait(cnode)
}
}
def generatePassThroughTrait(s_interface:String):String =
{
def passThroughTraitName(iName:String) =
"T" + (if(iName.startsWith("I")) iName.substring(1) else iName)
val tname = uniqueName(passThroughTraitName(simpleName(s_interface)))
val vname = "impl"
val iname = s_interface.replace('.', '/')
val idesc = "L"+iname+";"
val inode = classNode(s_interface)
if(inode == null) {
System.out.println("Unable to generate pass through trait for: "+s_interface+" class not found.")
return null
}
if((inode.access&ACC_INTERFACE) == 0) throw new IllegalArgumentException(s_interface+" is not an interface.")
val cw = new CC_ClassWriter(ASM4)
var mv:MethodVisitor = null
var fv:FieldVisitor = null
cw.visit(V1_6, ACC_PUBLIC|ACC_SUPER, tname, null, "codechicken/multipart/TileMultipart", Array(iname))
{
fv = cw.visitField(ACC_PRIVATE, vname, idesc, null, null)
fv.visitEnd()
}
{
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null)
mv.visitCode()
mv.visitVarInsn(ALOAD, 0)
mv.visitMethodInsn(INVOKESPECIAL, "codechicken/multipart/TileMultipart", "<init>", "()V")
mv.visitInsn(RETURN)
mv.visitMaxs(1, 1)
mv.visitEnd()
}
{
mv = cw.visitMethod(ACC_PUBLIC, "bindPart", "(Lcodechicken/multipart/TMultiPart;)V", null, null)
mv.visitCode()
mv.visitVarInsn(ALOAD, 0)
mv.visitVarInsn(ALOAD, 1)
mv.visitMethodInsn(INVOKESPECIAL, "codechicken/multipart/TileMultipart", "bindPart", "(Lcodechicken/multipart/TMultiPart;)V")
mv.visitVarInsn(ALOAD, 1)
mv.visitTypeInsn(INSTANCEOF, iname)
val l2 = new Label()
mv.visitJumpInsn(IFEQ, l2)
val l3 = new Label()
mv.visitLabel(l3)
mv.visitVarInsn(ALOAD, 0)
mv.visitVarInsn(ALOAD, 1)
mv.visitTypeInsn(CHECKCAST, iname)
mv.visitFieldInsn(PUTFIELD, tname, vname, idesc)
mv.visitLabel(l2)
mv.visitFrame(F_SAME, 0, null, 0, null)
mv.visitInsn(RETURN)
mv.visitMaxs(2, 2)
mv.visitEnd()
}
{
mv = cw.visitMethod(ACC_PUBLIC, "partRemoved", "(Lcodechicken/multipart/TMultiPart;I)V", null, null)
mv.visitCode()
mv.visitVarInsn(ALOAD, 0)
mv.visitVarInsn(ALOAD, 1)
mv.visitVarInsn(ILOAD, 2)
mv.visitMethodInsn(INVOKESPECIAL, "codechicken/multipart/TileMultipart", "partRemoved", "(Lcodechicken/multipart/TMultiPart;I)V")
mv.visitVarInsn(ALOAD, 1)
mv.visitVarInsn(ALOAD, 0)
mv.visitFieldInsn(GETFIELD, tname, vname, idesc)
val l2 = new Label()
mv.visitJumpInsn(IF_ACMPNE, l2)
val l3 = new Label()
mv.visitLabel(l3)
mv.visitVarInsn(ALOAD, 0)
mv.visitInsn(ACONST_NULL)
mv.visitFieldInsn(PUTFIELD, tname, vname, idesc)
mv.visitLabel(l2)
mv.visitFrame(F_SAME, 0, null, 0, null)
mv.visitInsn(RETURN)
mv.visitMaxs(3, 3)
mv.visitEnd()
}
{
mv = cw.visitMethod(ACC_PUBLIC, "canAddPart", "(Lcodechicken/multipart/TMultiPart;)Z", null, null)
mv.visitCode()
mv.visitVarInsn(ALOAD, 0)
mv.visitFieldInsn(GETFIELD, tname, vname, idesc)
val l1 = new Label()
mv.visitJumpInsn(IFNULL, l1)
mv.visitVarInsn(ALOAD, 1)
mv.visitTypeInsn(INSTANCEOF, iname)
mv.visitJumpInsn(IFEQ, l1)
val l2 = new Label()
mv.visitLabel(l2)
mv.visitInsn(ICONST_0)
mv.visitInsn(IRETURN)
mv.visitLabel(l1)
mv.visitFrame(F_SAME, 0, null, 0, null)
mv.visitVarInsn(ALOAD, 0)
mv.visitVarInsn(ALOAD, 1)
mv.visitMethodInsn(INVOKESPECIAL, "codechicken/multipart/TileMultipart", "canAddPart", "(Lcodechicken/multipart/TMultiPart;)Z")
mv.visitInsn(IRETURN)
mv.visitMaxs(2, 2)
mv.visitEnd()
}
def methods(cnode:ClassNode):Map[String, MethodNode] =
{
val m = cnode.methods.map(m => (m.name+m.desc, m)).toMap
if(cnode.interfaces != null)
m++cnode.interfaces.flatMap(i => methods(classNode(i)))
else
m
}
def generatePassThroughMethod(m:MethodNode)
{
mv = cw.visitMethod(ACC_PUBLIC, m.name, m.desc, m.signature, Array(m.exceptions:_*))
mv.visitVarInsn(ALOAD, 0)
mv.visitFieldInsn(GETFIELD, tname, vname, idesc)
finishBridgeCall(mv, m.desc, INVOKEINTERFACE, iname, m.name, m.desc)
}
methods(inode).values.foreach(generatePassThroughMethod)
cw.visitEnd()
internalDefine(tname, cw.toByteArray)
registerTrait(s_interface, tname, false)
return tname
}
def generateTile(types:Seq[String], client:Boolean) = SuperSet(types, client).generate
}
-213
View File
@@ -1,213 +0,0 @@
/* __ *\
** ________ ___ / / ___ Scala API **
** / __/ __// _ | / / / _ | (c) 2007-2013, LAMP/EPFL **
** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
** /____/\___/_/ |_/____/_/ | | **
** |/ **
\* */
package codechicken.multipart.asm
object ByteCodecs {
def avoidZero(src: Array[Byte]): Array[Byte] = {
var i = 0
val srclen = src.length
var count = 0
while (i < srclen) {
if (src(i) == 0x7f) count += 1
i += 1
}
val dst = new Array[Byte](srclen + count)
i = 0
var j = 0
while (i < srclen) {
val in = src(i)
if (in == 0x7f) {
dst(j) = (0xc0).toByte
dst(j + 1) = (0x80).toByte
j += 2
} else {
dst(j) = (in + 1).toByte
j += 1
}
i += 1
}
dst
}
def regenerateZero(src: Array[Byte]): Int = {
var i = 0
val srclen = src.length
var j = 0
while (i < srclen) {
val in: Int = src(i) & 0xff
if (in == 0xc0 && (src(i + 1) & 0xff) == 0x80) {
src(j) = 0x7f
i += 2
} else if (in == 0) {
src(j) = 0x7f
i += 1
} else {
src(j) = (in - 1).toByte
i += 1
}
j += 1
}
j
}
def encode8to7(src: Array[Byte]): Array[Byte] = {
val srclen = src.length
val dstlen = (srclen * 8 + 6) / 7
val dst = new Array[Byte](dstlen)
var i = 0
var j = 0
while (i + 6 < srclen) {
var in: Int = src(i) & 0xff
dst(j) = (in & 0x7f).toByte
var out: Int = in >>> 7
in = src(i + 1) & 0xff
dst(j + 1) = (out | (in << 1) & 0x7f).toByte
out = in >>> 6
in = src(i + 2) & 0xff
dst(j + 2) = (out | (in << 2) & 0x7f).toByte
out = in >>> 5
in = src(i + 3) & 0xff
dst(j + 3) = (out | (in << 3) & 0x7f).toByte
out = in >>> 4
in = src(i + 4) & 0xff
dst(j + 4) = (out | (in << 4) & 0x7f).toByte
out = in >>> 3
in = src(i + 5) & 0xff
dst(j + 5) = (out | (in << 5) & 0x7f).toByte
out = in >>> 2
in = src(i + 6) & 0xff
dst(j + 6) = (out | (in << 6) & 0x7f).toByte
out = in >>> 1
dst(j + 7) = out.toByte
i += 7
j += 8
}
if (i < srclen) {
var in: Int = src(i) & 0xff
dst(j) = (in & 0x7f).toByte; j += 1
var out: Int = in >>> 7
if (i + 1 < srclen) {
in = src(i + 1) & 0xff
dst(j) = (out | (in << 1) & 0x7f).toByte; j += 1
out = in >>> 6
if (i + 2 < srclen) {
in = src(i + 2) & 0xff
dst(j) = (out | (in << 2) & 0x7f).toByte; j += 1
out = in >>> 5
if (i + 3 < srclen) {
in = src(i + 3) & 0xff
dst(j) = (out | (in << 3) & 0x7f).toByte; j += 1
out = in >>> 4
if (i + 4 < srclen) {
in = src(i + 4) & 0xff
dst(j) = (out | (in << 4) & 0x7f).toByte; j += 1
out = in >>> 3
if (i + 5 < srclen) {
in = src(i + 5) & 0xff
dst(j) = (out | (in << 5) & 0x7f).toByte; j += 1
out = in >>> 2
}
}
}
}
}
if (j < dstlen) dst(j) = out.toByte
}
dst
}
def decode7to8(src: Array[Byte], srclen: Int): Int = {
var i = 0
var j = 0
val dstlen = (srclen * 7 + 7) / 8
while (i + 7 < srclen) {
var out: Int = src(i).toInt
var in: Byte = src(i + 1)
src(j) = (out | (in & 0x01) << 7).toByte
out = in >>> 1
in = src(i + 2)
src(j + 1) = (out | (in & 0x03) << 6).toByte
out = in >>> 2
in = src(i + 3)
src(j + 2) = (out | (in & 0x07) << 5).toByte
out = in >>> 3
in = src(i + 4)
src(j + 3) = (out | (in & 0x0f) << 4).toByte
out = in >>> 4
in = src(i + 5)
src(j + 4) = (out | (in & 0x1f) << 3).toByte
out = in >>> 5
in = src(i + 6)
src(j + 5) = (out | (in & 0x3f) << 2).toByte
out = in >>> 6
in = src(i + 7)
src(j + 6) = (out | in << 1).toByte
i += 8
j += 7
}
if (i < srclen) {
var out: Int = src(i).toInt
if (i + 1 < srclen) {
var in: Byte = src(i + 1)
src(j) = (out | (in & 0x01) << 7).toByte; j += 1
out = in >>> 1
if (i + 2 < srclen) {
in = src(i + 2)
src(j) = (out | (in & 0x03) << 6).toByte; j += 1
out = in >>> 2
if (i + 3 < srclen) {
in = src(i + 3)
src(j) = (out | (in & 0x07) << 5).toByte; j += 1
out = in >>> 3
if (i + 4 < srclen) {
in = src(i + 4)
src(j) = (out | (in & 0x0f) << 4).toByte; j += 1
out = in >>> 4
if (i + 5 < srclen) {
in = src(i + 5)
src(j) = (out | (in & 0x1f) << 3).toByte; j += 1
out = in >>> 5
if (i + 6 < srclen) {
in = src(i + 6)
src(j) = (out | (in & 0x3f) << 2).toByte; j += 1
out = in >>> 6
}
}
}
}
}
}
if (j < dstlen) src(j) = out.toByte
}
dstlen
}
def encode(xs: Array[Byte]): Array[Byte] = avoidZero(encode8to7(xs))
/**
* Destructively decodes array xs and returns the length of the decoded array.
*
* Sometimes returns (length+1) of the decoded array. Example:
*
* scala> val enc = scala.reflect.generic.ByteCodecs.encode(Array(1,2,3))
* enc: Array[Byte] = Array(2, 5, 13, 1)
*
* scala> scala.reflect.generic.ByteCodecs.decode(enc)
* res43: Int = 4
*
* scala> enc
* res44: Array[Byte] = Array(1, 2, 3, 0)
*
* However, this does not always happen.
*/
def decode(xs: Array[Byte]): Int = {
val len = regenerateZero(xs)
decode7to8(xs, len)
}
}
@@ -1,12 +0,0 @@
package codechicken.multipart.asm
import codechicken.multipart.TileMultipart
trait IMultipartFactory
{
def registerTrait(s_interface:String, s_trait:String, client:Boolean)
def generatePassThroughTrait(s_interface:String):String
def generateTile(types:Seq[String], client:Boolean):TileMultipart
}
@@ -1,341 +0,0 @@
package codechicken.multipart.asm
import org.objectweb.asm.tree.ClassNode
import org.objectweb.asm.tree.AnnotationNode
import org.objectweb.asm.tree.FieldNode
import org.objectweb.asm.tree.MethodNode
import java.util.{List => JList}
import scala.collection.mutable.{ListBuffer => MList}
import scala.collection.JavaConverters._
import scala.collection.JavaConversions._
import ScalaSignature._
object ScalaSignature
{
object Bytes
{
implicit def reader(bc:Bytes) = new ByteCodeReader(bc)
def apply(bytes:Array[Byte], pos:Int, len:Int) = new Bytes(bytes, pos, len)
def apply(bytes:Array[Byte]):Bytes = apply(bytes, 0, bytes.length)
}
class Bytes(val bytes:Array[Byte], val pos:Int, val len:Int)
{
def section() = bytes take pos drop len
}
trait Flags
{
def hasFlag(flag:Int):Boolean
def isPrivate = hasFlag(0x00000004)
def isProtected = hasFlag(0x00000008)
def isAbstract = hasFlag(0x00000080)
def isInterface = hasFlag(0x00000800)
def isMethod = hasFlag(0x00000200)
def isParam = hasFlag(0x00002000)
def isStatic = hasFlag(0x00800000)
def isTrait = hasFlag(0x02000000)
def isAccessor = hasFlag(0x08000000)
}
trait SymbolRef extends Flags
{
def full:String
def flags:Int
def hasFlag(flag:Int) = (flags&flag) != 0
}
case class ClassSymbol(name:String, owner:SymbolRef, flags:Int, info:Int) extends SymbolRef
{
override def toString = "ClassSymbol("+name+","+owner+","+flags.toHexString+","+info+")"
def full = owner.full+"."+name
def info(sig:ScalaSignature):ClassType = sig.evalT(info)
def jParent(sig:ScalaSignature) = info(sig).parent.jName
def jInterfaces(sig:ScalaSignature) = info(sig).interfaces.map(_.jName)
}
case class MethodSymbol(name:String, owner:SymbolRef, flags:Int, info:Int) extends SymbolRef
{
override def toString = "MethodSymbol("+name+","+owner+","+flags.toHexString+","+info+")"
def full = owner.full+"."+name
def info(sig:ScalaSignature):SMethodType = sig.evalT(info)
def jDesc(sig:ScalaSignature):String = info(sig).jDesc(sig)
}
case class ExternalSymbol(name:String) extends SymbolRef
{
override def toString = name
def full = name
def flags = 0
}
case object NoSymbol extends SymbolRef
{
def full = "<no symbol>"
def flags = 0
}
trait SMethodType
{
def jDesc(sig:ScalaSignature):String = "("+params.map(m => m.info(sig).returnType.jDesc).mkString+")"+returnType.jDesc
def returnType:TypeRef
def params:List[MethodSymbol]
}
case class ClassType(owner:SymbolRef, parents:List[TypeRef])
{
def parent = parents.head
def interfaces = parents.drop(1)
}
case class MethodType(returnType:TypeRef, params:List[MethodSymbol]) extends SMethodType
case class ParameterlessType(returnType:TypeRef) extends SMethodType
{
def params = List()
}
trait TypeRef
{
def name:String
def jName = name.replace('.', '/') match {
case "scala/AnyRef" => "java/lang/Object"
case s => s
}
def jDesc:String = name match
{
case "scala.Array" => null
case "scala.Long" => "J"
case "scala.Int" => "I"
case "scala.Short" => "S"
case "scala.Byte" => "B"
case "scala.Double" => "D"
case "scala.Float" => "F"
case "scala.Boolean" => "Z"
case "scala.Unit" => "V"
case _ => "L"+jName+";"
}
}
case class TypeRefType(owner:TypeRef, sym:SymbolRef, typArgs:List[TypeRef]) extends TypeRef with SMethodType
{
def params = List()
def returnType = this
def name = sym.full
override def jDesc = name match
{
case "scala.Array" => "["+typArgs(0).jDesc
case _ => super.jDesc
}
}
case class ThisType(sym:SymbolRef) extends TypeRef
{
def name = sym.full
}
case class SingleType(owner:TypeRef, sym:SymbolRef) extends TypeRef
{
def name = sym.full
}
case object NoType extends TypeRef
{
def name = "<no type>"
}
class SigEntry(val start:Int, val bytes:Bytes)
{
def id = bytes.bytes(start)
def delete()
{
bytes.bytes(start) = 3
}
}
}
case class ScalaSignature(major:Int, minor:Int, table:Array[SigEntry], bytes:Bytes)
{
def evalS(i:Int):String = {
val e = table(i)
val bc = e.bytes
val bcr = bc:ByteCodeReader
return e.id match
{
case 1|2 => bcr.readString(bc.len)
case 3 => NoSymbol.full
case 9|10 =>
var s = evalS(bcr.readNat)
if(bc.pos+bc.len > bcr.pos)
s = evalS(bcr.readNat)+"."+s
s
}
}
def evalT[T](i:Int):T = eval(i).asInstanceOf[T]
def evalList[T](bcr:ByteCodeReader) =
{
var l = MList[T]()
while(bcr.more)
l+=evalT(bcr.readNat)
l.toList
}
def eval(i:Int):Any = {//we only parse the ones we actually care about
val e = table(i)
val bc = e.bytes
val bcr = bc:ByteCodeReader
return e.id match
{
case 1|2 => evalS(i)
case 6 => ClassSymbol(evalS(bcr.readNat), evalT(bcr.readNat), bcr.readNat, bcr.readNat)
case 8 => MethodSymbol(evalS(bcr.readNat), evalT(bcr.readNat), bcr.readNat, bcr.readNat)
case 9|10 => ExternalSymbol(evalS(i))
case 11|12 => NoType //12 is actually NoPrefixType (no lower bound)
case 13 => ThisType(evalT(bcr.readNat))
case 14 => SingleType(evalT(bcr.readNat), evalT(bcr.readNat))
case 16 => TypeRefType(evalT(bcr.readNat), evalT(bcr.readNat), evalList(bcr))
case 19 => ClassType(evalT(bcr.readNat), evalList(bcr))
case 20 => MethodType(evalT(bcr.readNat), evalList(bcr))
case 21|48 => ParameterlessType(evalT(bcr.readNat))//48 is actually a bounded super type, but it should work fine for our purposes
case _ => NoSymbol
}
}
}
class ByteCodeReader(val bc:Bytes)
{
var pos = bc.pos
def more = pos < bc.pos+bc.len
def readString(len:Int) = advance(len)(new String(bc.bytes drop pos take len))
def readByte = advance(1)(bc.bytes(pos))
def readNat:Int =
{
var r = 0
var b = 0
do
{
b = readByte
r = r<<7|b&0x7F
}
while((b&0x80) != 0)
return r
}
def advance[A](len:Int)(r:A):A =
{
if(pos+len > bc.pos+bc.len)
throw new IllegalArgumentException("Ran off the end of bytecode")
pos+=len
return r
}
def readEntry =
{
val p = pos
val tpe:Int = readByte
val len = readNat
advance(len)(new SigEntry(p, new Bytes(bc.bytes, pos, len)))
}
def readSig =
{
val major = readByte
val minor = readByte
val table = new Array[SigEntry](readNat)
for(i <- 0 until table.size)
table(i) = readEntry
ScalaSignature(major, minor, table, bc)
}
}
object ScalaSigReader
{
def decode(s:String):Array[Byte] =
{
val bytes = s.getBytes
return bytes take ByteCodecs.decode(bytes)
}
def encode(b:Array[Byte]):String =
{
val bytes = ByteCodecs.encode8to7(b)
var i = 0
while(i < bytes.length)
{
bytes(i) = ((bytes(i)+1)&0x7F).toByte
i+=1
}
return new String(bytes.take(bytes.length-1), "UTF-8")
}
def read(ann:AnnotationNode):ScalaSignature = Bytes(decode(ann.values.get(1).asInstanceOf[String])).readSig
def write(sig:ScalaSignature, ann:AnnotationNode) = ann.values.set(1, encode(sig.bytes.bytes))
def ann(cnode:ClassNode):Option[AnnotationNode] = cnode.visibleAnnotations match {
case null => None
case a => a.find(ann => ann.desc.equals("Lscala/reflect/ScalaSignature;"))
}
}
class ScalaSigSideTransformer
{
def transform(ann:AnnotationNode, cnode:ClassNode, removedFields:JList[FieldNode], removedMethods:JList[MethodNode])
{
if(removedFields.isEmpty && removedMethods.isEmpty)
return
val remFields = removedFields.asScala.map(f => (f.name, f.desc.replace('$', '/')))
val remMethods = removedMethods.asScala.map(f => (f.name, f.desc.replace('$', '/')))
val sig = ScalaSigReader.read(ann)
for(i <- 0 until sig.table.length)
{
val e = sig.table(i)
if(e.id == 8)//check and remove
{
val sym:MethodSymbol = sig.evalT(i)
if(sym.isAccessor)
{
val fName = if(sym.name.endsWith("_$eq")) sym.name.substring(0, sym.name.length-4) else sym.name
if(remFields.find(t => t._1 == sym.name.trim).nonEmpty)
{
e.delete()
val it = cnode.methods.iterator
while(it.hasNext)
{
val m = it.next
if(m.name == sym.name && m.desc == sym.jDesc(sig))
it.remove()
}
}
}
else if(sym.isMethod)
{
if(remMethods.find(t => t._1 == sym.name && t._2 == sym.jDesc(sig)).nonEmpty)
e.delete()
}
else//field
{
if(remFields.find(t => t._1 == sym.name.trim).nonEmpty)
e.delete()
}
}
}
ScalaSigReader.write(sig, ann)
}
}
@@ -1,314 +0,0 @@
package codechicken.multipart.asm
import scala.collection.mutable.{Map => MMap, ListBuffer => MList}
import org.objectweb.asm.tree._
import org.objectweb.asm.Opcodes._
import org.objectweb.asm.Type
import org.objectweb.asm.Type._
import scala.collection.JavaConversions._
object StackAnalyser
{
def width(t:Type):Int = t.getSize
def width(s:String):Int = width(Type.getType(s))
def width(it:Iterable[Type]):Int = it.foldLeft(0)(_+width(_))
abstract class StackEntry(implicit val insn:AbstractInsnNode)
{
def getType:Type
}
abstract class LocalEntry
{
def getType:Type
}
case class This(owner:Type) extends LocalEntry
{
def getType = owner
}
case class Param(i:Int, t:Type) extends LocalEntry
{
def getType = t
}
case class Store(e:StackEntry)(implicit val insn:AbstractInsnNode) extends LocalEntry
{
def getType = e.getType
}
case class Const(c:Any)(implicit insn:AbstractInsnNode) extends StackEntry
{
def getType = c match {
case o:Byte => BYTE_TYPE
case o:Short => SHORT_TYPE
case o:Int => INT_TYPE
case o:Long => LONG_TYPE
case o:Float => FLOAT_TYPE
case o:Double => DOUBLE_TYPE
case o:Char => CHAR_TYPE
case o:Boolean => BOOLEAN_TYPE
case o:String => getObjectType("java/lang/String")
case null => getObjectType("java/lang/Object")
case _ => throw new IllegalArgumentException("Unknown const "+c)
}
}
case class Load(e:LocalEntry)(implicit insn:AbstractInsnNode) extends StackEntry
{
def getType = e.getType
}
case class UnaryOp(op:Int, e:StackEntry)(implicit insn:AbstractInsnNode) extends StackEntry
{
def getType = e.getType
}
case class BinaryOp(op:Int, e2:StackEntry, e1:StackEntry)(implicit insn:AbstractInsnNode) extends StackEntry
{
def getType = e1.getType
}
case class PrimitiveCast(e:StackEntry, t:Type)(implicit insn:AbstractInsnNode) extends StackEntry
{
def getType = t
}
case class ReturnAddress()(implicit insn:AbstractInsnNode) extends StackEntry
{
def getType = INT_TYPE
}
case class GetField(obj:StackEntry, field:FieldInsnNode)(implicit insn:AbstractInsnNode) extends StackEntry
{
def getType = Type.getType(field.desc)
}
case class Invoke(op:Int, params:Array[StackEntry], obj:StackEntry, method:MethodInsnNode)(implicit insn:AbstractInsnNode) extends StackEntry
{
def getType = Type.getMethodType(method.desc).getReturnType
}
case class New(t:Type)(implicit insn:AbstractInsnNode) extends StackEntry
{
def getType = t
}
case class NewArray(len:StackEntry, t:Type)(implicit insn:AbstractInsnNode) extends StackEntry
{
def getType = t
}
case class ArrayLength(array:StackEntry)(implicit insn:AbstractInsnNode) extends StackEntry
{
def getType = INT_TYPE
}
case class ArrayLoad(index:StackEntry, e:StackEntry)(implicit insn:AbstractInsnNode) extends StackEntry
{
def getType = e.getType.getElementType
}
case class Cast(obj:StackEntry, t:Type)(implicit insn:AbstractInsnNode) extends StackEntry
{
def getType = t
}
case class NewMultiArray(sizes:Array[StackEntry], t:Type)(implicit insn:AbstractInsnNode) extends StackEntry
{
def getType = t
}
case class CaughtException(t:Type)(implicit insn:AbstractInsnNode) extends StackEntry
{
def getType = t
}
}
class StackAnalyser(val owner:Type, val m:MethodNode)
{
import StackAnalyser._
val stack = MList[StackEntry]()
val locals = MList[LocalEntry]()
private val catchHandlers = MMap[LabelNode, TryCatchBlockNode]()
{
if((m.access & ACC_STATIC) == 0)
pushL(This(owner))
val ptypes = getArgumentTypes(m.desc)
for(i <- 0 until ptypes.length)
pushL(Param(i, ptypes(i)))
m.tryCatchBlocks.foreach(b => catchHandlers.put(b.handler, b))
}
def pushL(entry:LocalEntry) = setL(locals.size, entry)
def setL(i:Int, entry:LocalEntry) =
{
while(i+entry.getType.getSize > locals.size) locals += null
locals(i) = entry
if(entry.getType.getSize == 2)
locals(i+1) = entry
}
def push(entry:StackEntry) = insert(0, entry)
def _pop(i:Int = 0) = stack.remove(stack.size-i-1)
def pop(i:Int = 0) =
{
val e = _pop(i)
if(e.getType.getSize == 2)
{
if(peek(i) != e) throw new IllegalStateException("Wide stack entry elems don't match ("+e+","+peek(i))
_pop(i)
}
e
}
def peek(i:Int = 0) = stack(stack.size-i-1)
def insert(i:Int, entry:StackEntry)
{
if(entry.getType.getSize == 0)
return
stack.insert(stack.size-i, entry)
if(entry.getType.getSize == 2)
stack.insert(stack.size-i, entry)
}
def popArgs(desc:String) =
{
val t = getType(desc)
val args = new Array[StackEntry](t.getArgumentTypes.length)
for(i <- 0 until args.length)
args(args.length-i-1) = pop()
args
}
def visitInsn(ainsn:AbstractInsnNode)
{
implicit val thisInsn = ainsn//passes to any StackEntry we create
ainsn match {
case insn:InsnNode => ainsn.getOpcode match
{
case ACONST_NULL => push(Const(null))
case ICONST_M1 => push(Const(-1))
case ICONST_0 => push(Const(0))
case ICONST_1 => push(Const(1))
case ICONST_2 => push(Const(2))
case ICONST_3 => push(Const(3))
case ICONST_4 => push(Const(4))
case ICONST_5 => push(Const(5))
case LCONST_0 => push(Const(0L))
case LCONST_1 => push(Const(1L))
case FCONST_0 => push(Const(0F))
case FCONST_1 => push(Const(1F))
case FCONST_2 => push(Const(2F))
case DCONST_0 => push(Const(0D))
case DCONST_1 => push(Const(1D))
case i if i >= IALOAD && i <= SALOAD =>
push(ArrayLoad(pop(), pop()))
case i if i >= IASTORE && i <= SASTORE =>
pop(); pop(); pop()
case POP => _pop()
case POP2 => _pop(); _pop()
case DUP => push(peek())
case DUP_X1 => insert(2, peek())
case DUP_X2 => insert(3, peek())
case DUP2 => push(peek(1)); push(peek(1))
case DUP2_X1 => insert(3, peek(1)); insert(3, peek())
case DUP2_X2 => insert(4, peek(1)); insert(4, peek())
case SWAP => push(pop(1))
case i if i >= IADD && i <= DREM =>
push(BinaryOp(i, pop(), pop()))
case i if i >= INEG && i <= DNEG =>
push(UnaryOp(i, pop()))
case i if i >= ISHL && i <= LXOR =>
push(BinaryOp(i, pop(), pop()))
case L2I|F2I|D2I => push(PrimitiveCast(pop(), DOUBLE_TYPE))
case I2L|F2L|D2L => push(PrimitiveCast(pop(), LONG_TYPE))
case I2F|L2F|D2F => push(PrimitiveCast(pop(), FLOAT_TYPE))
case I2D|L2D|F2D => push(PrimitiveCast(pop(), DOUBLE_TYPE))
case I2B => push(PrimitiveCast(pop(), BYTE_TYPE))
case I2C => push(PrimitiveCast(pop(), CHAR_TYPE))
case I2S => push(PrimitiveCast(pop(), SHORT_TYPE))
case i if i >= LCMP && i <= DCMPG =>
push(BinaryOp(i, pop(), pop()))
case i if i >= IRETURN && i <= ARETURN => pop()
case ARRAYLENGTH => push(ArrayLength(pop()))
case ATHROW => pop()
case MONITORENTER|MONITOREXIT => pop()
case _ =>
}
case iinsn:IntInsnNode => ainsn.getOpcode match
{
case BIPUSH => push(Const(iinsn.operand.toByte))
case SIPUSH => push(Const(iinsn.operand.toShort))
}
case ldcinsn:LdcInsnNode => ainsn.getOpcode match
{
case LDC => push(Const(ldcinsn.cst))
}
case vinsn:VarInsnNode => ainsn.getOpcode match
{
case i if i >= ILOAD && i <= ALOAD =>
push(Load(locals(vinsn.`var`)))
case i if i >= ISTORE && i <= ASTORE =>
setL(vinsn.`var`, Store(pop()))
}
case incinsn:IincInsnNode => ainsn.getOpcode match
{
case IINC => setL(incinsn.`var`, Store(BinaryOp(IINC, Const(incinsn.incr), Load(locals(incinsn.`var`)))))
}
case jinsn:JumpInsnNode => ainsn.getOpcode match
{
case i if i >= IFEQ && i <= IFLE => pop()
case i if i >= IF_ICMPEQ && i <= IF_ACMPNE => pop(); pop()
case JSR => push(ReturnAddress())
case IFNULL|IFNONNULL => pop()
case GOTO =>
}
case sinsn:TableSwitchInsnNode => pop()
case linsn:LookupSwitchInsnNode => pop()
case finsn:FieldInsnNode => ainsn.getOpcode match
{
case GETSTATIC => push(GetField(null, finsn))
case PUTSTATIC => pop()
case GETFIELD => push(GetField(pop(), finsn))
case PUTFIELD => pop(); pop()
}
case minsn:MethodInsnNode => ainsn.getOpcode match
{
case INVOKEVIRTUAL|INVOKESPECIAL|INVOKEINTERFACE =>
push(Invoke(ainsn.getOpcode, popArgs(minsn.desc), pop(), minsn))
case INVOKESTATIC =>
push(Invoke(ainsn.getOpcode, popArgs(minsn.desc), null, minsn))
}
case tinsn:TypeInsnNode => ainsn.getOpcode match
{
case NEW => push(New(getType(tinsn.desc)))
case NEWARRAY => push(NewArray(pop(), getType(tinsn.desc)))
case ANEWARRAY => push(NewArray(pop(), getType("["+tinsn.desc)))
case CHECKCAST => push(Cast(pop(), getType(tinsn.desc)))
case INSTANCEOF => push(UnaryOp(INSTANCEOF, pop()))
}
case mainsn:MultiANewArrayInsnNode =>
val sizes = new Array[StackEntry](mainsn.dims)
for(i <- 0 until sizes.length)
sizes(i) = pop()
push(NewMultiArray(sizes, getType(mainsn.desc)))
/*case fnode:FrameNode => fnode.`type` match
{
case F_NEW|F_FULL => println("reset stacks/locals")
case F_APPEND => println("add local")
case F_CHOP => println("rem locals")
case F_SAME => println("reset")
case F_SAME1 => println("reset locals and all but bottom stack")
}*/
case lnode:LabelNode =>
catchHandlers.get(lnode) match
{
case Some(tcblock) => push(CaughtException(Type.getType(tcblock.`type`)))
case None =>
}
case _ =>
}
}
}
@@ -1,33 +0,0 @@
package codechicken.multipart.handler
import net.minecraft.world.World
import cpw.mods.fml.common.FMLCommonHandler
import net.minecraft.block.Block
object MultipartCompatiblity
{
var canAddPart = (world:World, x:Int, y:Int, z:Int) => true
def load() {
if(FMLCommonHandler.instance().getModName.contains("mcpc"))
MCPCCompatModule.load()
}
}
object MCPCCompatModule
{
def load() {
try {
val m_canPlacePart = classOf[World].getDeclaredMethod("canPlaceMultipart", classOf[Block], classOf[Int], classOf[Int], classOf[Int])
MultipartCompatiblity.canAddPart = (world:World, x:Int, y:Int, z:Int) => {
m_canPlacePart.invoke(world, MultipartProxy.block, x:Integer, y:Integer, z:Integer).asInstanceOf[Boolean].booleanValue()
}
} catch {
case e:Exception =>
System.err.println("Failed to integrate MCPC placement hooks")
e.printStackTrace()
}
}
}
@@ -1,67 +0,0 @@
package codechicken.multipart.handler
import codechicken.multipart.TileMultipart
import cpw.mods.fml.common.eventhandler.{EventPriority, SubscribeEvent}
import net.minecraft.server.MinecraftServer
import codechicken.lib.packet.PacketCustom
import codechicken.multipart.MultiPartRegistry
import net.minecraftforge.event.world._
import java.util.EnumSet
import scala.collection.JavaConverters._
import java.util.List
import net.minecraft.entity.player.EntityPlayerMP
import cpw.mods.fml.relauncher.SideOnly
import cpw.mods.fml.relauncher.Side
import net.minecraftforge.client.event.DrawBlockHighlightEvent
import codechicken.multipart.BlockMultipart
import net.minecraft.util.MovingObjectPosition
import net.minecraft.util.MovingObjectPosition.MovingObjectType
import cpw.mods.fml.common.gameevent.TickEvent
object MultipartEventHandler
{
@SubscribeEvent(priority = EventPriority.HIGHEST)
def tileEntityLoad(event:ChunkDataEvent.Load)
{
MultipartSaveLoad.loadTiles(event.getChunk)
}
@SubscribeEvent
def worldUnLoad(event:WorldEvent.Unload)
{
MultipartSPH.onWorldUnload(event.world)
}
@SubscribeEvent
def chunkWatch(event:ChunkWatchEvent.Watch)
{
MultipartSPH.onChunkWatch(event.player, event.chunk)
}
@SubscribeEvent
def chunkUnWatch(event:ChunkWatchEvent.UnWatch)
{
MultipartSPH.onChunkUnWatch(event.player, event.chunk)
}
@SubscribeEvent
@SideOnly(Side.CLIENT)
def drawBlockHighlight(event:DrawBlockHighlightEvent)
{
if(event.target != null && event.target.typeOfHit == MovingObjectType.BLOCK &&
event.player.worldObj.getTileEntity(event.target.blockX, event.target.blockY, event.target.blockZ).isInstanceOf[TileMultipart])
{
if(BlockMultipart.drawHighlight(event.player.worldObj, event.player, event.target, event.partialTicks))
event.setCanceled(true)
}
}
@SubscribeEvent
def serverTick(event:TickEvent.ServerTickEvent)
{
if(event.phase == TickEvent.Phase.END)
MultipartSPH.onTickEnd(
MinecraftServer.getServer.getConfigurationManager.playerEntityList
.asInstanceOf[List[EntityPlayerMP]].asScala)
}
}
@@ -1,42 +0,0 @@
package codechicken.multipart.handler
import cpw.mods.fml.common.Mod
import cpw.mods.fml.common.event.FMLPostInitializationEvent
import cpw.mods.fml.common.event.FMLPreInitializationEvent
import cpw.mods.fml.common.event.FMLInitializationEvent
import cpw.mods.fml.common.Mod.EventHandler
import cpw.mods.fml.common.event.FMLServerAboutToStartEvent
import codechicken.multipart.MultiPartRegistry
@Mod(modid = "ForgeMultipart", acceptedMinecraftVersions = "[1.7.2]",
modLanguage="scala")
object MultipartMod
{
@EventHandler
def preInit(event:FMLPreInitializationEvent)
{
MultipartProxy.preInit(event.getModConfigurationDirectory)
}
@EventHandler
def init(event:FMLInitializationEvent)
{
MultipartProxy.init()
}
@EventHandler
def postInit(event:FMLPostInitializationEvent)
{
if(MultiPartRegistry.required)
{
MultiPartRegistry.postInit()
MultipartProxy.postInit()
}
}
@EventHandler
def beforeServerStart(event:FMLServerAboutToStartEvent)
{
MultiPartRegistry.beforeServerStart()
}
}
@@ -1,69 +0,0 @@
package codechicken.multipart.handler
import net.minecraft.tileentity.TileEntity
import net.minecraft.nbt.NBTTagCompound
import java.util.Map
import net.minecraft.world.chunk.Chunk
import net.minecraft.world.ChunkPosition
import codechicken.multipart.TileMultipart
import codechicken.lib.asm.ObfMapping
import net.minecraft.world.World
/**
* Hack due to lack of TileEntityLoadEvent in forge
*/
object MultipartSaveLoad
{
var loadingWorld: World = _
class TileNBTContainer extends TileEntity
{
var tag: NBTTagCompound = _
override def readFromNBT(t: NBTTagCompound) {
super.readFromNBT(t)
tag = t
}
}
def hookLoader() {
val field = classOf[TileEntity].getDeclaredField(
new ObfMapping("net/minecraft/tileentity/TileEntity", "field_145855_i", "Ljava/util/Map;")
.toRuntime.s_name)
field.setAccessible(true)
val map = field.get(null).asInstanceOf[Map[String, Class[_ <: TileEntity]]]
map.put("savedMultipart", classOf[TileNBTContainer])
}
private val classToNameMap = getClassToNameMap
def registerTileClass(t: Class[_ <: TileEntity]) {
classToNameMap.put(t, "savedMultipart")
}
def getClassToNameMap = {
val field = classOf[TileEntity].getDeclaredField(
new ObfMapping("net/minecraft/tileentity/TileEntity", "field_145853_j", "Ljava/util/Map;")
.toRuntime.s_name)
field.setAccessible(true)
field.get(null).asInstanceOf[Map[Class[_ <: TileEntity], String]]
}
def loadTiles(chunk: Chunk) {
loadingWorld = chunk.worldObj
val iterator = chunk.chunkTileEntityMap.asInstanceOf[Map[ChunkPosition, TileEntity]].entrySet.iterator
while (iterator.hasNext) {
val e = iterator.next
if (e.getValue.isInstanceOf[TileNBTContainer]) {
val t = TileMultipart.createFromNBT(e.getValue.asInstanceOf[TileNBTContainer].tag)
if (t != null) {
t.setWorldObj(e.getValue.getWorldObj)
t.validate()
e.setValue(t)
}
else
iterator.remove()
}
}
}
}
@@ -1,180 +0,0 @@
package codechicken.multipart.handler
import codechicken.lib.packet.PacketCustom.{IHandshakeHandler, IClientPacketHandler, IServerPacketHandler}
import codechicken.lib.packet.PacketCustom
import net.minecraft.client.Minecraft
import codechicken.multipart.MultiPartRegistry
import net.minecraft.entity.player.EntityPlayerMP
import codechicken.multipart.ControlKeyModifer
import net.minecraft.world.World
import net.minecraft.world.chunk.Chunk
import java.util.{Map => JMap}
import java.util.Iterator
import net.minecraft.tileentity.TileEntity
import codechicken.multipart.TileMultipart
import net.minecraft.entity.player.EntityPlayer
import scala.collection.mutable.{Map, Set, HashMap, MultiMap}
import codechicken.lib.vec.BlockCoord
import codechicken.lib.data.MCOutputStreamWrapper
import java.io.DataOutputStream
import java.io.ByteArrayOutputStream
import net.minecraft.world.ChunkCoordIntPair
import MultipartProxy._
import codechicken.multipart.PacketScheduler
import java.util.LinkedList
import scala.collection.JavaConversions._
import net.minecraft.network.play.{INetHandlerPlayServer, INetHandlerPlayClient}
import net.minecraft.network.play.server.S40PacketDisconnect
import net.minecraft.util.ChatComponentTranslation
import net.minecraft.network.NetHandlerPlayServer
class MultipartPH
{
val channel = MultipartMod
val registryChannel = "ForgeMultipart"
}
object MultipartCPH extends MultipartPH with IClientPacketHandler
{
def handlePacket(packet: PacketCustom, mc: Minecraft, netHandler:INetHandlerPlayClient) {
packet.getType match {
case 1 => handlePartRegistration(packet, netHandler)
case 2 => handleCompressedTileDesc(packet, mc.theWorld)
case 3 => handleCompressedTileData(packet, mc.theWorld)
}
}
def handlePartRegistration(packet: PacketCustom, netHandler: INetHandlerPlayClient) {
val missing = MultiPartRegistry.readIDMap(packet)
if (!missing.isEmpty)
netHandler.handleDisconnect(new S40PacketDisconnect(new ChatComponentTranslation("multipart.missing", missing.mkString(", "))))
}
def handleCompressedTileDesc(packet: PacketCustom, world: World) {
val cc = new ChunkCoordIntPair(packet.readInt, packet.readInt)
val num = packet.readUShort
for (i <- 0 until num)
TileMultipart.handleDescPacket(world, indexInChunk(cc, packet.readShort), packet)
}
def handleCompressedTileData(packet: PacketCustom, world: World) {
var x = packet.readInt
while (x != Int.MaxValue) {
val pos = new BlockCoord(x, packet.readInt, packet.readInt)
var i = packet.readUByte
while (i < 255) {
TileMultipart.handlePacket(pos, world, i, packet)
i = packet.readUByte
}
x = packet.readInt
}
TileMultipart.flushClientCache()
}
}
object MultipartSPH extends MultipartPH with IServerPacketHandler with IHandshakeHandler
{
class MCByteStream(bout: ByteArrayOutputStream) extends MCOutputStreamWrapper(new DataOutputStream(bout))
{
def getBytes = bout.toByteArray
}
private val updateMap = Map[World, Map[BlockCoord, MCByteStream]]()
private val chunkWatchers = new HashMap[EntityPlayer, Set[ChunkCoordIntPair]] with MultiMap[EntityPlayer, ChunkCoordIntPair]
private val newWatchers = Map[EntityPlayer, LinkedList[ChunkCoordIntPair]]()
def handlePacket(packet: PacketCustom, sender: EntityPlayerMP, netHandler:INetHandlerPlayServer) {
packet.getType match {
case 1 => ControlKeyModifer.map.put(sender, packet.readBoolean)
}
}
def handshakeRecieved(netHandler: NetHandlerPlayServer) {
val packet = new PacketCustom(registryChannel, 1)
MultiPartRegistry.writeIDMap(packet)
netHandler.sendPacket(packet.toPacket)
}
def onWorldUnload(world: World) {
if (!world.isRemote)
updateMap.remove(world)
}
def getTileStream(world: World, pos: BlockCoord) =
updateMap.getOrElseUpdate(world, {
if (world.isRemote)
throw new IllegalArgumentException("Cannot use MultipartSPH on a client world")
Map()
}).getOrElseUpdate(pos, {
val s = new MCByteStream(new ByteArrayOutputStream)
s.writeCoord(pos)
s
})
def onTickEnd(players: Seq[EntityPlayerMP]) {
PacketScheduler.sendScheduled()
for (p <- players if chunkWatchers.containsKey(p)) {
updateMap.get(p.worldObj) match {
case Some(m) if !m.isEmpty =>
val chunks = chunkWatchers(p)
val packet = new PacketCustom(channel, 3).compress()
var send = false
for ((pos, stream) <- m if chunks(new ChunkCoordIntPair(pos.x >> 4, pos.z >> 4))) {
send = true
packet.writeByteArray(stream.getBytes)
packet.writeByte(255) //terminator
}
if (send) {
packet.writeInt(Int.MaxValue) //terminator
packet.sendToPlayer(p)
}
case _ =>
}
}
updateMap.foreach(_._2.clear())
for ((p, chunks) <- newWatchers) {
chunks.foreach {
c =>
val chunk = p.worldObj.getChunkFromChunkCoords(c.chunkXPos, c.chunkZPos)
val pkt = getDescPacket(chunk, chunk.chunkTileEntityMap.asInstanceOf[JMap[_, TileEntity]].values.iterator)
if (pkt != null) pkt.sendToPlayer(p)
chunkWatchers.addBinding(p, c)
}
chunks.clear()
}
}
def onChunkWatch(player: EntityPlayer, c: ChunkCoordIntPair) {
newWatchers.getOrElseUpdate(player, new LinkedList).add(c)
}
def onChunkUnWatch(player: EntityPlayer, c: ChunkCoordIntPair) {
newWatchers.get(player) match {
case Some(chunks) => chunks.remove(c)
case _ =>
}
chunkWatchers.removeBinding(player, c)
}
def getDescPacket(chunk: Chunk, it: Iterator[TileEntity]): PacketCustom = {
val s = new MCByteStream(new ByteArrayOutputStream)
var num = 0
while (it.hasNext) {
val tile = it.next
if (tile.isInstanceOf[TileMultipart]) {
s.writeShort(indexInChunk(new BlockCoord(tile)))
tile.asInstanceOf[TileMultipart].writeDesc(s)
num += 1
}
}
if (num != 0) {
return new PacketCustom(channel, 2).compress()
.writeInt(chunk.xPosition).writeInt(chunk.zPosition)
.writeShort(num)
.writeByteArray(s.getBytes)
}
return null
}
}
@@ -1,92 +0,0 @@
package codechicken.multipart.handler
import codechicken.multipart._
import cpw.mods.fml.client.registry.RenderingRegistry
import cpw.mods.fml.client.registry.ClientRegistry
import net.minecraft.tileentity.TileEntity
import codechicken.lib.config.ConfigFile
import java.io.File
import codechicken.multipart.handler.MultipartProxy._
import net.minecraftforge.common.MinecraftForge
import cpw.mods.fml.relauncher.SideOnly
import cpw.mods.fml.relauncher.Side
import codechicken.lib.packet.PacketCustom
import net.minecraft.world.ChunkCoordIntPair
import codechicken.lib.vec.BlockCoord
import codechicken.lib.world.WorldExtensionManager
import cpw.mods.fml.common.FMLCommonHandler
import cpw.mods.fml.common.registry.GameRegistry
import net.minecraft.block.Block
class MultipartProxy_serverImpl
{
def preInit(cfgdir:File)
{
config = new ConfigFile(new File(cfgdir, "multipart.cfg"))
.setComment("Multipart API config file")
GameRegistry.registerBlock(new BlockMultipart().setBlockName("multipart"), null, "block")
block = Block.blockRegistry.getObject("ForgeMultipart:block").asInstanceOf[BlockMultipart]
MultipartGenerator.registerTrait("codechicken.multipart.TSlottedPart", "codechicken.multipart.scalatraits.TSlottedTile")
MultipartGenerator.registerTrait("net.minecraftforge.fluids.IFluidHandler", "codechicken.multipart.scalatraits.TFluidHandlerTile")
MultipartGenerator.registerTrait("net.minecraft.inventory.IInventory", "codechicken.multipart.scalatraits.JInventoryTile")
MultipartGenerator.registerTrait("net.minecraft.inventory.ISIdedInventory", "codechicken.multipart.scalatraits.JInventoryTile")
MultipartGenerator.registerTrait("codechicken.multipart.JPartialOcclusion", "codechicken.multipart.scalatraits.TPartialOcclusionTile")
MultipartGenerator.registerTrait("codechicken.multipart.IRedstonePart", "codechicken.multipart.scalatraits.TRedstoneTile")
MultipartGenerator.registerTrait("codechicken.multipart.IRandomDisplayTick", "codechicken.multipart.scalatraits.TRandomDisplayTickTile", null)
MultipartGenerator.registerTrait("codechicken.multipart.INeighborTileChange", null, "codechicken.multipart.scalatraits.TTileChangeTile")
MultipartSaveLoad.hookLoader()
}
def init(){}
def postInit()
{
FMLCommonHandler.instance().bus().register(MultipartEventHandler)
MinecraftForge.EVENT_BUS.register(MultipartEventHandler)
PacketCustom.assignHandler(MultipartSPH.channel, MultipartSPH)
PacketCustom.assignHandshakeHandler(MultipartSPH.registryChannel, MultipartSPH)
WorldExtensionManager.registerWorldExtension(TickScheduler)
MultipartCompatiblity.load()
}
def onTileClassBuilt(t:Class[_ <: TileEntity])
{
MultipartSaveLoad.registerTileClass(t)
}
}
class MultipartProxy_clientImpl extends MultipartProxy_serverImpl
{
@SideOnly(Side.CLIENT)
override def postInit()
{
super.postInit()
RenderingRegistry.registerBlockHandler(MultipartRenderer)
PacketCustom.assignHandler(MultipartCPH.channel, MultipartCPH)
PacketCustom.assignHandler(MultipartCPH.registryChannel, MultipartCPH)
FMLCommonHandler.instance().bus().register(ControlKeyHandler)
ClientRegistry.registerKeyBinding(ControlKeyHandler)
}
@SideOnly(Side.CLIENT)
override def onTileClassBuilt(t:Class[_ <: TileEntity])
{
super.onTileClassBuilt(t)
ClientRegistry.bindTileEntitySpecialRenderer(t, MultipartRenderer)
}
}
object MultipartProxy extends MultipartProxy_clientImpl
{
var block:BlockMultipart = _
var config:ConfigFile = _
def indexInChunk(cc:ChunkCoordIntPair, i:Int) = new BlockCoord(cc.chunkXPos<<4|i&0xF, (i>>8)&0xFF, cc.chunkZPos<<4|(i&0xF0)>>4)
def indexInChunk(pos:BlockCoord) = pos.x&0xF|pos.y<<8|(pos.z&0xF)<<4
}
@@ -1,184 +0,0 @@
package codechicken.multipart.minecraft;
import codechicken.lib.vec.BlockCoord;
import codechicken.lib.vec.Cuboid6;
import codechicken.lib.vec.Vector3;
import codechicken.multipart.IFaceRedstonePart;
import codechicken.multipart.TickScheduler;
import net.minecraft.block.Block;
import net.minecraft.block.BlockButton;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.projectile.EntityArrow;
import net.minecraft.init.Blocks;
import net.minecraft.item.ItemStack;
import net.minecraft.util.MovingObjectPosition;
import net.minecraft.world.World;
import net.minecraftforge.common.util.ForgeDirection;
public class ButtonPart extends McSidedMetaPart implements IFaceRedstonePart
{
public static BlockButton stoneButton = (BlockButton) Blocks.stone_button;
public static BlockButton woodenButton = (BlockButton) Blocks.wooden_button;
public static int[] metaSideMap = new int[]{-1, 4, 5, 2, 3};
public static int[] sideMetaMap = new int[]{-1, -1, 3, 4, 1, 2};
public static BlockButton getButton(int meta)
{
return (meta&0x10) > 0 ? woodenButton : stoneButton;
}
public ButtonPart()
{
}
public ButtonPart(int meta)
{
super(meta);
}
@Override
public int sideForMeta(int meta)
{
return metaSideMap[meta&7];
}
@Override
public Block getBlock()
{
return getButton(meta);
}
@Override
public String getType()
{
return "mc_button";
}
public int delay()
{
return sensitive() ? 30 : 20;
}
public boolean sensitive()
{
return (meta&0x10) > 0;
}
@Override
public Cuboid6 getBounds()
{
int m = meta & 7;
double d = pressed() ? 0.0625 : 0.125;
if (m == 1)
return new Cuboid6(0.0, 0.375, 0.5 - 0.1875, d, 0.625, 0.5 + 0.1875);
if (m == 2)
return new Cuboid6(1.0 - d, 0.375, 0.5 - 0.1875, 1.0, 0.625, 0.5 + 0.1875);
if (m == 3)
return new Cuboid6(0.5 - 0.1875, 0.375, 0.0, 0.5 + 0.1875, 0.625, d);
if (m == 4)
return new Cuboid6(0.5 - 0.1875, 0.375, 1.0 - d, 0.5 + 0.1875, 0.625, 1.0);
return null;//falloff
}
public static McBlockPart placement(World world, BlockCoord pos, int side, int type)
{
if(side == 0 || side == 1)
return null;
pos = pos.copy().offset(side^1);
if(!world.isSideSolid(pos.x, pos.y, pos.z, ForgeDirection.getOrientation(side)))
return null;
return new ButtonPart(sideMetaMap[side^1]|type<<4);
}
@Override
public boolean activate(EntityPlayer player, MovingObjectPosition part, ItemStack item)
{
if(pressed())
return false;
if(!world().isRemote)
toggle();
return true;
}
@Override
public void scheduledTick()
{
if(pressed())
updateState();
}
public boolean pressed()
{
return (meta&8) > 0;
}
@Override
public void onEntityCollision(Entity entity)
{
if(!pressed() && !world().isRemote && entity instanceof EntityArrow)
updateState();
}
private void toggle()
{
boolean in = !pressed();
meta^=8;
world().playSoundEffect(x() + 0.5, y() + 0.5, z() + 0.5, "random.click", 0.3F, in ? 0.6F : 0.5F);
if(in)
scheduleTick(delay());
sendDescUpdate();
tile().notifyPartChange(this);
tile().notifyNeighborChange(metaSideMap[meta&7]);
tile().markDirty();
}
private void updateState()
{
boolean arrows = sensitive() && !world().getEntitiesWithinAABB(EntityArrow.class,
getBounds().add(Vector3.fromTileEntity(tile())).toAABB()).isEmpty();
boolean pressed = pressed();
if(arrows != pressed)
toggle();
if(arrows && pressed)
scheduleTick(delay());
}
@Override
public void onRemoved()
{
if(pressed())
tile().notifyNeighborChange(metaSideMap[meta&7]);
}
@Override
public int weakPowerLevel(int side)
{
return pressed() ? 15 : 0;
}
@Override
public int strongPowerLevel(int side)
{
return pressed() && side == metaSideMap[meta&7] ? 15 : 0;
}
@Override
public boolean canConnectRedstone(int side)
{
return true;
}
@Override
public int getFace() {
return metaSideMap[meta&7];
}
}
@@ -1,63 +0,0 @@
package codechicken.multipart.minecraft;
import net.minecraft.block.Block;
import net.minecraft.init.Blocks;
import net.minecraft.world.World;
import codechicken.lib.vec.BlockCoord;
import codechicken.multipart.MultiPartRegistry.IPartConverter;
import codechicken.multipart.MultiPartRegistry.IPartFactory;
import codechicken.multipart.MultiPartRegistry;
import codechicken.multipart.TMultiPart;
import java.util.Arrays;
public class Content implements IPartFactory, IPartConverter
{
@Override
public TMultiPart createPart(String name, boolean client)
{
if(name.equals("mc_torch")) return new TorchPart();
if(name.equals("mc_lever")) return new LeverPart();
if(name.equals("mc_button")) return new ButtonPart();
if(name.equals("mc_redtorch")) return new RedstoneTorchPart();
return null;
}
public void init()
{
MultiPartRegistry.registerConverter(this);
MultiPartRegistry.registerParts(this, new String[]{
"mc_torch",
"mc_lever",
"mc_button",
"mc_redtorch"
});
}
@Override
public Iterable<Block> blockTypes() {
return Arrays.asList(Blocks.torch, Blocks.lever, Blocks.stone_button, Blocks.wooden_button, Blocks.redstone_torch, Blocks.unlit_redstone_torch);
}
@Override
public TMultiPart convert(World world, BlockCoord pos)
{
Block b = world.getBlock(pos.x, pos.y, pos.z);
int meta = world.getBlockMetadata(pos.x, pos.y, pos.z);
if(b == Blocks.torch)
return new TorchPart(meta);
if(b == Blocks.lever)
return new LeverPart(meta);
if(b == Blocks.stone_button)
return new ButtonPart(meta);
if(b == Blocks.wooden_button)
return new ButtonPart(meta|0x10);
if(b == Blocks.redstone_torch)
return new RedstoneTorchPart(meta);
if(b == Blocks.unlit_redstone_torch)
return new RedstoneTorchPart(meta|0x10);
return null;
}
}
@@ -1,123 +0,0 @@
package codechicken.multipart.minecraft;
import codechicken.lib.packet.PacketCustom;
import codechicken.lib.raytracer.RayTracer;
import codechicken.lib.vec.BlockCoord;
import codechicken.lib.vec.Vector3;
import codechicken.multipart.TileMultipart;
import net.minecraft.block.Block;
import net.minecraft.block.BlockFence;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.item.ItemStack;
import net.minecraft.network.play.client.C08PacketPlayerBlockPlacement;
import net.minecraft.util.MovingObjectPosition;
import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.event.entity.player.PlayerDestroyItemEvent;
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
import net.minecraftforge.event.entity.player.PlayerInteractEvent.Action;
public class EventHandler
{
private ThreadLocal<Object> placing = new ThreadLocal<Object>();
@SubscribeEvent
public void playerInteract(PlayerInteractEvent event)
{
if(event.action == Action.RIGHT_CLICK_BLOCK && event.entityPlayer.worldObj.isRemote)
{
if(placing.get() != null)
return;//for mods that do dumb stuff and call this event like MFR
placing.set(event);
if(place(event.entityPlayer, event.entityPlayer.worldObj))
event.setCanceled(true);
placing.set(null);
}
}
public static boolean place(EntityPlayer player, World world)
{
MovingObjectPosition hit = RayTracer.reTrace(world, player);
if(hit == null)
return false;
BlockCoord pos = new BlockCoord(hit.blockX, hit.blockY, hit.blockZ).offset(hit.sideHit);
ItemStack held = player.getHeldItem();
McBlockPart part = null;
if(held == null)
return false;
Block heldBlock = Block.getBlockFromItem(held.getItem());
if(heldBlock == Blocks.air)
return false;
if(heldBlock == Blocks.torch)
part = TorchPart.placement(world, pos, hit.sideHit);
else if(heldBlock == Blocks.lever)
part = LeverPart.placement(world, pos, player, hit.sideHit);
else if(heldBlock == Blocks.stone_button)
part = ButtonPart.placement(world, pos, hit.sideHit, 0);
else if(heldBlock == Blocks.wooden_button)
part = ButtonPart.placement(world, pos, hit.sideHit, 1);
else if(heldBlock == Blocks.redstone_torch)
part = RedstoneTorchPart.placement(world, pos, hit.sideHit);
if(part == null)
return false;
if(world.isRemote && !player.isSneaking())//attempt to use block activated like normal and tell the server the right stuff
{
Vector3 f = new Vector3(hit.hitVec).add(-hit.blockX, -hit.blockY, -hit.blockZ);
Block block = world.getBlock(hit.blockX, hit.blockY, hit.blockZ);
if(!ignoreActivate(block) && block.onBlockActivated(world, hit.blockX, hit.blockY, hit.blockZ, player, hit.sideHit, (float)f.x, (float)f.y, (float)f.z))
{
player.swingItem();
PacketCustom.sendToServer(new C08PacketPlayerBlockPlacement(
hit.blockX, hit.blockY, hit.blockZ, hit.sideHit,
player.inventory.getCurrentItem(),
(float)f.x, (float)f.y, (float)f.z));
return false;
}
}
TileMultipart tile = TileMultipart.getOrConvertTile(world, pos);
if(tile == null || !tile.canAddPart(part))
return false;
if(!world.isRemote)
{
TileMultipart.addPart(world, pos, part);
world.playSoundEffect(pos.x + 0.5, pos.y + 0.5, pos.z + 0.5,
part.getBlock().stepSound.func_150496_b(),
(part.getBlock().stepSound.getVolume() + 1.0F) / 2.0F,
part.getBlock().stepSound.getPitch() * 0.8F);
if(!player.capabilities.isCreativeMode)
{
held.stackSize--;
if (held.stackSize == 0)
{
player.inventory.mainInventory[player.inventory.currentItem] = null;
MinecraftForge.EVENT_BUS.post(new PlayerDestroyItemEvent(player, held));
}
}
}
else
{
player.swingItem();
new PacketCustom(McMultipartSPH.channel, 1).sendToServer();
}
return true;
}
/**
* Because vanilla is weird.
*/
private static boolean ignoreActivate(Block block)
{
if(block instanceof BlockFence)
return true;
return false;
}
}
@@ -1,16 +0,0 @@
package codechicken.multipart.minecraft;
import codechicken.lib.vec.BlockCoord;
import net.minecraft.block.Block;
import net.minecraft.world.World;
public interface IPartMeta
{
public int getMetadata();
public World getWorld();
public Block getBlock();
public BlockCoord getPos();
}
@@ -1,152 +0,0 @@
package codechicken.multipart.minecraft;
import net.minecraft.block.Block;
import net.minecraft.block.BlockLever;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.item.ItemStack;
import net.minecraft.util.MovingObjectPosition;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraftforge.common.util.ForgeDirection;
import codechicken.lib.vec.BlockCoord;
import codechicken.lib.vec.Cuboid6;
import codechicken.multipart.IFaceRedstonePart;
public class LeverPart extends McSidedMetaPart implements IFaceRedstonePart
{
public static BlockLever lever = (BlockLever) Blocks.lever;
public static int[] metaSideMap = new int[]{1, 4, 5, 2, 3, 0, 0, 1};
public static int[] sideMetaMap = new int[]{6, 0, 3, 4, 1, 2};
public static int[] metaSwapMap = new int[]{5, 7};
public LeverPart()
{
}
public LeverPart(int meta)
{
super(meta);
}
@Override
public Block getBlock()
{
return lever;
}
@Override
public String getType()
{
return "mc_lever";
}
public boolean active()
{
return (meta&8) > 0;
}
@Override
public Cuboid6 getBounds()
{
int m = meta & 7;
double d = 0.1875;
if (m == 1)
return new Cuboid6(0, 0.2, 0.5 - d, d * 2, 0.8, 0.5 + d);
if (m == 2)
return new Cuboid6(1 - d * 2, 0.2, 0.5 - d, 1, 0.8, 0.5 + d);
if (m == 3)
return new Cuboid6(0.5 - d, 0.2, 0, 0.5 + d, 0.8, d * 2);
if (m == 4)
return new Cuboid6(0.5 - d, 0.2, 1 - d * 2, 0.5 + d, 0.8, 1);
d = 0.25;
if (m == 0 || m == 7)
return new Cuboid6(0.5 - d, 0.4, 0.5 - d, 0.5 + d, 1, 0.5 + d);
return new Cuboid6(0.5 - d, 0, 0.5 - d, 0.5 + d, 0.6, 0.5 + d);
}
@Override
public int sideForMeta(int meta)
{
return metaSideMap[meta&7];
}
public static McBlockPart placement(World world, BlockCoord pos, EntityPlayer player, int side)
{
pos = pos.copy().offset(side^1);
if(!world.isSideSolid(pos.x, pos.y, pos.z, ForgeDirection.getOrientation(side)))
return null;
int meta = sideMetaMap[side^1];
if(side < 2 && ((int)(player.rotationYaw / 90 + 0.5) & 1) == 0)
meta = metaSwapMap[side^1];
return new LeverPart(meta);
}
@Override
public boolean activate(EntityPlayer player, MovingObjectPosition part, ItemStack item)
{
World world = world();
if(world.isRemote)
return true;
world.playSoundEffect(x() + 0.5, y() + 0.5, z() + 0.5, "random.click", 0.3F, !active() ? 0.6F : 0.5F);
meta ^= 8;
sendDescUpdate();
tile().notifyPartChange(this);
tile().notifyNeighborChange(metaSideMap[meta&7]);
tile().markDirty();
return true;
}
@Override
public void drawBreaking(RenderBlocks renderBlocks)
{
IBlockAccess actual = renderBlocks.blockAccess;
renderBlocks.blockAccess = new PartMetaAccess(this);
renderBlocks.renderBlockLever(lever, x(), y(), z());
renderBlocks.blockAccess = actual;
}
@Override
public void onRemoved()
{
if(active())
tile().notifyNeighborChange(metaSideMap[meta&7]);
}
@Override
public void onConverted()
{
if(active())
tile().notifyNeighborChange(metaSideMap[meta&7]);
}
@Override
public int weakPowerLevel(int side)
{
return active() ? 15 : 0;
}
@Override
public int strongPowerLevel(int side)
{
return active() && side == metaSideMap[meta&7] ? 15 : 0;
}
@Override
public boolean canConnectRedstone(int side)
{
return true;
}
@Override
public int getFace() {
return metaSideMap[meta&7];
}
}
@@ -1,92 +0,0 @@
package codechicken.multipart.minecraft;
import java.util.Arrays;
import java.util.Collections;
import net.minecraft.block.Block;
import net.minecraft.client.particle.EffectRenderer;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.util.IIcon;
import net.minecraft.util.MovingObjectPosition;
import codechicken.lib.vec.Cuboid6;
import codechicken.multipart.IconHitEffects;
import codechicken.multipart.JCuboidPart;
import codechicken.multipart.JIconHitEffects;
import codechicken.multipart.JNormalOcclusion;
import codechicken.multipart.NormalOcclusionTest;
import codechicken.multipart.TMultiPart;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
public abstract class McBlockPart extends JCuboidPart implements JNormalOcclusion, JIconHitEffects
{
public abstract Block getBlock();
@Override
public boolean occlusionTest(TMultiPart npart)
{
return NormalOcclusionTest.apply(this, npart);
}
@Override
public Iterable<Cuboid6> getOcclusionBoxes()
{
return Arrays.asList(getBounds());
}
@Override
public Iterable<Cuboid6> getCollisionBoxes()
{
return Collections.emptyList();
}
@Override
public Iterable<ItemStack> getDrops()
{
return Arrays.asList(new ItemStack(getBlock()));
}
@Override
public ItemStack pickItem(MovingObjectPosition hit)
{
return new ItemStack(getBlock());
}
@Override
public float getStrength(MovingObjectPosition hit, EntityPlayer player)
{
return getBlock().getPlayerRelativeBlockHardness(player, player.worldObj, hit.blockX, hit.blockY, hit.blockZ)*30;
}
@Override
public IIcon getBreakingIcon(Object subPart, int side)
{
return getBlock().getIcon(0, 0);
}
@Override
@SideOnly(Side.CLIENT)
public IIcon getBrokenIcon(int side)
{
return getBlock().getIcon(0, 0);
}
@Override
public void addHitEffects(MovingObjectPosition hit, EffectRenderer effectRenderer)
{
IconHitEffects.addHitEffects(this, hit, effectRenderer);
}
@Override
public void addDestroyEffects(MovingObjectPosition hit, EffectRenderer effectRenderer)
{
IconHitEffects.addDestroyEffects(this, effectRenderer, false);
}
@Override
public int getLightValue()
{
return getBlock().getLightValue();
}
}
@@ -1,82 +0,0 @@
package codechicken.multipart.minecraft;
import codechicken.lib.data.MCDataInput;
import codechicken.lib.data.MCDataOutput;
import codechicken.lib.lighting.LightMatrix;
import codechicken.lib.vec.BlockCoord;
import codechicken.lib.vec.Vector3;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.World;
public abstract class McMetaPart extends McBlockPart implements IPartMeta
{
public byte meta;
public McMetaPart()
{
}
public McMetaPart(int meta)
{
this.meta = (byte)meta;
}
@Override
public void save(NBTTagCompound tag)
{
tag.setByte("meta", meta);
}
@Override
public void load(NBTTagCompound tag)
{
meta = tag.getByte("meta");
}
@Override
public void writeDesc(MCDataOutput packet)
{
packet.writeByte(meta);
}
@Override
public void readDesc(MCDataInput packet)
{
meta = packet.readByte();
}
@Override
public World getWorld()
{
return world();
}
@Override
public int getMetadata()
{
return meta;
}
@Override
public BlockCoord getPos()
{
return new BlockCoord(tile());
}
@Override
public boolean doesTick()
{
return false;
}
@Override
public boolean renderStatic(Vector3 pos, int pass)
{
if(pass == 0) {
new RenderBlocks(new PartMetaAccess(this)).renderBlockByRenderType(getBlock(), x(), y(), z());
return true;
}
return false;
}
}
@@ -1,30 +0,0 @@
package codechicken.multipart.minecraft;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.WorldClient;
import codechicken.lib.packet.PacketCustom;
import codechicken.lib.packet.PacketCustom.IClientPacketHandler;
import codechicken.lib.vec.BlockCoord;
import net.minecraft.network.play.INetHandlerPlayClient;
public class McMultipartCPH implements IClientPacketHandler
{
public static Object channel = MinecraftMultipartMod.instance;
@Override
public void handlePacket(PacketCustom packet, Minecraft mc, INetHandlerPlayClient netHandler) {
switch (packet.getType()) {
case 1:
spawnBurnoutSmoke(mc.theWorld, packet.readCoord());
break;
}
}
private void spawnBurnoutSmoke(WorldClient world, BlockCoord pos) {
for (int l = 0; l < 5; l++)
world.spawnParticle("smoke",
pos.x + world.rand.nextDouble() * 0.6 + 0.2,
pos.y + world.rand.nextDouble() * 0.6 + 0.2,
pos.z + world.rand.nextDouble() * 0.6 + 0.2, 0, 0, 0);
}
}
@@ -1,25 +0,0 @@
package codechicken.multipart.minecraft;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.network.play.INetHandlerPlayServer;
import net.minecraft.world.World;
import codechicken.lib.packet.PacketCustom;
import codechicken.lib.packet.PacketCustom.IServerPacketHandler;
public class McMultipartSPH implements IServerPacketHandler
{
public static Object channel = MinecraftMultipartMod.instance;
@Override
public void handlePacket(PacketCustom packet, EntityPlayerMP sender, INetHandlerPlayServer netHandler) {
switch (packet.getType()) {
case 1:
EventHandler.place(sender, sender.worldObj);
break;
}
}
public static void spawnBurnoutSmoke(World world, int x, int y, int z) {
new PacketCustom(channel, 1).writeCoord(x, y, z).sendToChunk(world, x >> 4, z >> 4);
}
}
@@ -1,69 +0,0 @@
package codechicken.multipart.minecraft;
import net.minecraft.item.ItemStack;
import net.minecraftforge.common.util.ForgeDirection;
import codechicken.lib.vec.BlockCoord;
import codechicken.lib.vec.Vector3;
import codechicken.multipart.TFacePart;
import codechicken.multipart.TileMultipart;
public abstract class McSidedMetaPart extends McMetaPart implements TFacePart
{
public McSidedMetaPart()
{
}
public McSidedMetaPart(int meta)
{
super(meta);
}
public abstract int sideForMeta(int meta);
@Override
public void onNeighborChanged()
{
if(!world().isRemote)
dropIfCantStay();
}
public boolean canStay()
{
BlockCoord pos = new BlockCoord(tile()).offset(sideForMeta(meta));
return world().isSideSolid(pos.x, pos.y, pos.z, ForgeDirection.getOrientation(sideForMeta(meta)^1));
}
public boolean dropIfCantStay()
{
if(!canStay())
{
drop();
return true;
}
return false;
}
public void drop()
{
TileMultipart.dropItem(new ItemStack(getBlock()), world(), Vector3.fromTileEntityCenter(tile()));
tile().remPart(this);
}
@Override
public int getSlotMask()
{
return 1<<sideForMeta(meta);
}
@Override
public boolean solid(int side)
{
return false;
}
@Override
public int redstoneConductionMap()
{
return 0x1F;
}
}
@@ -1,26 +0,0 @@
package codechicken.multipart.minecraft;
import net.minecraftforge.common.MinecraftForge;
import codechicken.lib.packet.PacketCustom;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.Instance;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
@Mod(modid = "McMultipart", acceptedMinecraftVersions="[1.7.2]")
public class MinecraftMultipartMod
{
@Instance("McMultipart")
public static MinecraftMultipartMod instance;
@Mod.EventHandler
public void preInit(FMLPreInitializationEvent event)
{
new Content().init();
MinecraftForge.EVENT_BUS.register(new EventHandler());
PacketCustom.assignHandler(this, new McMultipartSPH());
if(FMLCommonHandler.instance().getSide().isClient())
PacketCustom.assignHandler(this, new McMultipartCPH());
}
}
@@ -1,88 +0,0 @@
package codechicken.multipart.minecraft;
import codechicken.lib.vec.BlockCoord;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Vec3Pool;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.biome.BiomeGenBase;
import net.minecraftforge.common.util.ForgeDirection;
public class PartMetaAccess implements IBlockAccess {
public IPartMeta part;
private BlockCoord pos;
public PartMetaAccess(IPartMeta p) {
part = p;
pos = p.getPos();
}
@Override
public Block getBlock(int i, int j, int k) {
if (i == pos.x && j == pos.y && k == pos.z)
return part.getBlock();
return part.getWorld().getBlock(i, j, k);
}
@Override
public TileEntity getTileEntity(int i, int j, int k) {
if (i == pos.x && j == pos.y && k == pos.z)
throw new IllegalArgumentException("Unsupported Operation");
return part.getWorld().getTileEntity(i, j, k);
}
@Override
@SideOnly(Side.CLIENT)
public int getLightBrightnessForSkyBlocks(int i, int j, int k, int l) {
return part.getWorld().getLightBrightnessForSkyBlocks(i, j, k, l);
}
@Override
public int getBlockMetadata(int i, int j, int k) {
if (i == pos.x && j == pos.y && k == pos.z)
return part.getMetadata() & 0xF;
return part.getWorld().getBlockMetadata(i, j, k);
}
@Override
@SideOnly(Side.CLIENT)
public boolean isAirBlock(int i, int j, int k) {
throw new IllegalArgumentException("Unsupported Operation");
}
@Override
@SideOnly(Side.CLIENT)
public BiomeGenBase getBiomeGenForCoords(int i, int j) {
return part.getWorld().getBiomeGenForCoords(i, j);
}
@Override
@SideOnly(Side.CLIENT)
public int getHeight() {
return part.getWorld().getHeight();
}
@Override
@SideOnly(Side.CLIENT)
public boolean extendedLevelsInChunkCache() {
return part.getWorld().extendedLevelsInChunkCache();
}
@Override
public Vec3Pool getWorldVec3Pool() {
return part.getWorld().getWorldVec3Pool();
}
@Override
public int isBlockProvidingPowerTo(int i, int j, int k, int l) {
throw new IllegalArgumentException("Unsupported Operation");
}
@Override
public boolean isSideSolid(int x, int y, int z, ForgeDirection side, boolean _default) {
return part.getWorld().isSideSolid(x, y, z, side, _default);
}
}
@@ -1,246 +0,0 @@
package codechicken.multipart.minecraft;
import java.util.Random;
import net.minecraft.block.Block;
import net.minecraft.block.BlockRedstoneTorch;
import net.minecraft.init.Blocks;
import net.minecraft.item.ItemStack;
import net.minecraft.util.MovingObjectPosition;
import net.minecraft.world.World;
import net.minecraftforge.common.util.ForgeDirection;
import codechicken.lib.vec.BlockCoord;
import codechicken.lib.vec.Cuboid6;
import codechicken.multipart.IFaceRedstonePart;
import codechicken.multipart.IRandomUpdateTick;
import codechicken.multipart.RedstoneInteractions;
public class RedstoneTorchPart extends TorchPart implements IFaceRedstonePart, IRandomUpdateTick
{
public static BlockRedstoneTorch torchActive = (BlockRedstoneTorch) Blocks.redstone_torch;
public static BlockRedstoneTorch torchIdle = (BlockRedstoneTorch) Blocks.unlit_redstone_torch;
public class BurnoutEntry
{
public BurnoutEntry(long l)
{
timeout = l;
}
long timeout;
BurnoutEntry next;
}
private BurnoutEntry burnout;
public RedstoneTorchPart()
{
}
public RedstoneTorchPart(int meta)
{
super(meta);
}
@Override
public Block getBlock()
{
return active() ? torchActive : torchIdle;
}
public boolean active()
{
return (meta&0x10) > 0;
}
@Override
public String getType()
{
return "mc_redtorch";
}
@Override
public int sideForMeta(int meta)
{
return super.sideForMeta(meta&7);
}
@Override
public Cuboid6 getBounds()
{
return getBounds(meta&7);
}
public static McBlockPart placement(World world, BlockCoord pos, int side)
{
if(side == 0)
return null;
pos = pos.copy().offset(side^1);
if(!world.isSideSolid(pos.x, pos.y, pos.z, ForgeDirection.getOrientation(side)))
return null;
return new RedstoneTorchPart(sideMetaMap[side^1]|0x10);
}
@Override
public void randomDisplayTick(Random random)
{
if(!active())
return;
double d0 = x() + 0.5 + (random.nextFloat() - 0.5) * 0.2;
double d1 = y() + 0.7 + (random.nextFloat() - 0.5) * 0.2;
double d2 = z() + 0.5 + (random.nextFloat() - 0.5) * 0.2;
double d3 = 0.22D;
double d4 = 0.27D;
World world = world();
int m = meta&7;
if (m == 1)
world.spawnParticle("reddust", d0 - d4, d1 + d3, d2, 0, 0, 0);
else if (m == 2)
world.spawnParticle("reddust", d0 + d4, d1 + d3, d2, 0, 0, 0);
else if (m == 3)
world.spawnParticle("reddust", d0, d1 + d3, d2 - d4, 0, 0, 0);
else if (m == 4)
world.spawnParticle("reddust", d0, d1 + d3, d2 + d4, 0, 0, 0);
else
world.spawnParticle("reddust", d0, d1, d2, 0, 0, 0);
}
@Override
public ItemStack pickItem(MovingObjectPosition hit)
{
return new ItemStack(torchActive);
}
@Override
public void onNeighborChanged()
{
if(!world().isRemote)
{
if(!dropIfCantStay() && isBeingPowered() == active())
scheduleTick(2);
}
}
public boolean isBeingPowered()
{
int side = metaSideMap[meta&7];
return RedstoneInteractions.getPowerTo(this, side) > 0;
}
@Override
public void scheduledTick()
{
if(!world().isRemote && isBeingPowered() == active())
toggle();
}
@Override
public void randomUpdate()
{
scheduledTick();
}
private boolean burnedOut(boolean add)
{
long time = world().getTotalWorldTime();
while(burnout != null && burnout.timeout <= time)
burnout = burnout.next;
if(add)
{
BurnoutEntry e = new BurnoutEntry(world().getTotalWorldTime()+60);
if(burnout == null)
burnout = e;
else
{
BurnoutEntry b = burnout;
while(b.next != null)
b = b.next;
b.next = e;
}
}
if(burnout == null)
return false;
int i = 0;
BurnoutEntry b = burnout;
while(b != null)
{
i++;
b = b.next;
}
return i >= 8;
}
private void toggle()
{
if(active())//deactivating
{
if(burnedOut(true))
{
World world = world();
Random rand = world.rand;
world.playSoundEffect(x()+0.5, y()+0.5, z()+0.5, "random.fizz", 0.5F, 2.6F + (rand.nextFloat() - rand.nextFloat()) * 0.8F);
McMultipartSPH.spawnBurnoutSmoke(world, x(), y(), z());
}
}
else if(burnedOut(false))
{
return;
}
meta ^= 0x10;
sendDescUpdate();
tile().markDirty();
tile().notifyPartChange(this);
tile().notifyNeighborChange(1);
}
@Override
public void drop() {
meta|=0x10;//set state to on for drop
super.drop();
}
@Override
public void onRemoved()
{
if(active())
tile().notifyNeighborChange(1);
}
@Override
public void onAdded()
{
if(active())
tile().notifyNeighborChange(1);
onNeighborChanged();
}
@Override
public int strongPowerLevel(int side)
{
return side == 1 && active() ? 15 : 0;
}
@Override
public int weakPowerLevel(int side)
{
return active() && side != metaSideMap[meta&7] ? 15 : 0;
}
@Override
public boolean canConnectRedstone(int side)
{
return true;
}
@Override
public int getFace() {
return metaSideMap[meta&7];
}
}
@@ -1,122 +0,0 @@
package codechicken.multipart.minecraft;
import java.util.Random;
import net.minecraft.block.Block;
import net.minecraft.block.BlockTorch;
import net.minecraft.init.Blocks;
import net.minecraft.world.World;
import codechicken.multipart.IRandomDisplayTick;
import codechicken.lib.vec.BlockCoord;
import codechicken.lib.vec.Cuboid6;
public class TorchPart extends McSidedMetaPart implements IRandomDisplayTick
{
public static BlockTorch torch = (BlockTorch) Blocks.torch;
public static int[] metaSideMap = new int[]{-1, 4, 5, 2, 3, 0};
public static int[] sideMetaMap = new int[]{5, 0, 3, 4, 1, 2};
public TorchPart()
{
}
public TorchPart(int meta)
{
super(meta);
}
@Override
public Block getBlock()
{
return torch;
}
@Override
public String getType()
{
return "mc_torch";
}
@Override
public Cuboid6 getBounds()
{
return getBounds(meta);
}
public Cuboid6 getBounds(int meta)
{
double d = 0.15;
if (meta == 1)
return new Cuboid6(0, 0.2, 0.5 - d, d * 2, 0.8, 0.5 + d);
if (meta == 2)
return new Cuboid6(1 - d * 2, 0.2, 0.5 - d, 1, 0.8, 0.5 + d);
if (meta == 3)
return new Cuboid6(0.5 - d, 0.2, 0, 0.5 + d, 0.8, d * 2);
if (meta == 4)
return new Cuboid6(0.5 - d, 0.2, 1 - d * 2, 0.5 + d, 0.8, 1);
d = 0.1;
return new Cuboid6(0.5 - d, 0, 0.5 - d, 0.5 + d, 0.6, 0.5 + d);
}
@Override
public int sideForMeta(int meta)
{
return metaSideMap[meta];
}
@Override
public boolean canStay()
{
return sideForMeta(meta) == 0 && world().getBlock(x(), y() - 1, z()).canPlaceTorchOnTop(world(), x(), y() - 1, z()) || super.canStay();
}
public static McBlockPart placement(World world, BlockCoord pos, int side)
{
if(side == 0)
return null;
pos = pos.copy().offset(side^1);
Block block = world.getBlock(pos.x, pos.y, pos.z);
if(!block.isBlockSolid(world, pos.x, pos.y, pos.z, side) && (side != 1 || block.canPlaceTorchOnTop(world, pos.x, pos.y, pos.z)))
return null;
return new TorchPart(sideMetaMap[side^1]);
}
@Override
public void randomDisplayTick(Random random)
{
double d0 = x() + 0.5;
double d1 = y() + 0.7;
double d2 = z() + 0.5;
double d3 = 0.22D;
double d4 = 0.27D;
World world = world();
if (meta == 1)
{
world.spawnParticle("smoke", d0 - d4, d1 + d3, d2, 0, 0, 0);
world.spawnParticle("flame", d0 - d4, d1 + d3, d2, 0, 0, 0);
}
else if (meta == 2)
{
world.spawnParticle("smoke", d0 + d4, d1 + d3, d2, 0, 0, 0);
world.spawnParticle("flame", d0 + d4, d1 + d3, d2, 0, 0, 0);
}
else if (meta == 3)
{
world.spawnParticle("smoke", d0, d1 + d3, d2 - d4, 0, 0, 0);
world.spawnParticle("flame", d0, d1 + d3, d2 - d4, 0, 0, 0);
}
else if (meta == 4)
{
world.spawnParticle("smoke", d0, d1 + d3, d2 + d4, 0, 0, 0);
world.spawnParticle("flame", d0, d1 + d3, d2 + d4, 0, 0, 0);
}
else
{
world.spawnParticle("smoke", d0, d1, d2, 0, 0, 0);
world.spawnParticle("flame", d0, d1, d2, 0, 0, 0);
}
}
}
@@ -1,44 +0,0 @@
package codechicken.multipart.nei;
/* Commented until NEI is setup with maven
import codechicken.microblock.MicroblockClass;
import codechicken.microblock.MicroblockClassRegistry;
import codechicken.nei.api.IConfigureNEI;
public class NEI_MicroblockConfig implements IConfigureNEI
{
@Override
public void loadConfig()
{
MicroblockClass[] microClasses = MicroblockClassRegistry.classes();
for(int c = 0; c < microClasses.length; c++)
{
MicroblockClass mcrClass = microClasses[c];
if(mcrClass == null)
continue;
addSubset(mcrClass, c<<8|1);
addSubset(mcrClass, c<<8|2);
addSubset(mcrClass, c<<8|4);
}
}
private void addSubset(MicroblockClass mcrClass, int i)
{
API.addSetRange("Microblocks."+LangUtil.translateG(mcrClass.getName()+"."+(i&0xFF)+".subset"),
new MultiItemRange().add(MicroblockProxy.itemMicro(), i, i));
}
@Override
public String getName()
{
return "ForgeMultipart";
}
@Override
public String getVersion()
{
return "1.0.0.0";
}
}*/
@@ -1,130 +0,0 @@
package codechicken.multipart.scalatraits
import net.minecraftforge.common.util.ForgeDirection
import codechicken.multipart.TMultiPart
import codechicken.multipart.TileMultipart
import net.minecraftforge.fluids.IFluidHandler
import net.minecraftforge.fluids.FluidStack
import net.minecraftforge.fluids.FluidTankInfo
import net.minecraftforge.fluids.Fluid
import java.util.LinkedList
import scala.collection.JavaConversions._
/**
* Mixin trait implementation for parts implementing IFluidHandler.
* Distributes fluid manipulation among fluid handling parts.
*/
trait TFluidHandlerTile extends TileMultipart with IFluidHandler
{
var tankList = new LinkedList[IFluidHandler]()
override def copyFrom(that:TileMultipart)
{
super.copyFrom(that)
if(that.isInstanceOf[TFluidHandlerTile])
tankList = that.asInstanceOf[TFluidHandlerTile].tankList
}
override def bindPart(part:TMultiPart)
{
super.bindPart(part)
if(part.isInstanceOf[IFluidHandler])
tankList+=part.asInstanceOf[IFluidHandler]
}
override def partRemoved(part:TMultiPart, p:Int)
{
super.partRemoved(part, p)
if(part.isInstanceOf[IFluidHandler])
tankList-=part.asInstanceOf[IFluidHandler]
}
override def clearParts()
{
super.clearParts()
tankList.clear()
}
override def getTankInfo(dir:ForgeDirection):Array[FluidTankInfo] =
{
var tankCount:Int = 0
tankList.foreach(t => tankCount += t.getTankInfo(dir).length)
val tanks = new Array[FluidTankInfo](tankCount)
var i = 0
tankList.foreach(p => p.getTankInfo(dir).foreach{t =>
tanks(i) = t
i+=1
})
return tanks
}
override def fill(dir:ForgeDirection, liquid:FluidStack, doFill:Boolean):Int =
{
var filled = 0
val initial = liquid.amount
tankList.foreach(p =>
filled+=p.fill(dir, copy(liquid, initial-filled), doFill)
)
return filled
}
override def canFill(dir:ForgeDirection, liquid:Fluid) = tankList.find(_.canFill(dir, liquid)).isDefined
override def canDrain(dir:ForgeDirection, liquid:Fluid) = tankList.find(_.canDrain(dir, liquid)).isDefined
private def copy(liquid:FluidStack, quantity:Int):FluidStack =
{
val copy = liquid.copy
copy.amount = quantity
return copy
}
override def drain(dir:ForgeDirection, amount:Int, doDrain:Boolean):FluidStack =
{
var drained:FluidStack = null
var d_amount = 0
tankList.foreach{p =>
val drain = amount-d_amount
val ret = p.drain(dir, drain, false)
if(ret != null && ret.amount > 0 && (drained == null || drained.isFluidEqual(ret)))
{
if(doDrain)
p.drain(dir, drain, true)
if(drained == null)
drained = ret
d_amount+=ret.amount
}
}
if(drained != null)
drained.amount = d_amount
return drained
}
override def drain(dir:ForgeDirection, fluid:FluidStack, doDrain:Boolean):FluidStack =
{
val amount = fluid.amount
var drained:FluidStack = null
var d_amount = 0
tankList.foreach{p =>
val drain = copy(fluid, amount-d_amount)
val ret = p.drain(dir, drain, false)
if(ret != null && ret.amount > 0 && (drained == null || drained.isFluidEqual(ret)))
{
if(doDrain)
p.drain(dir, drain, true)
if(drained == null)
drained = ret
d_amount+=ret.amount
}
}
if(drained != null)
drained.amount = d_amount
return drained
}
}
@@ -1,133 +0,0 @@
package codechicken.multipart.scalatraits
import net.minecraft.inventory.{IInventory, ISidedInventory}
import codechicken.multipart.{TMultiPart, TileMultipart}
import java.util.LinkedList
import net.minecraft.item.ItemStack
import net.minecraft.entity.player.EntityPlayer
import scala.collection.JavaConversions._
import scala.collection.mutable.ArrayBuffer
trait TIInventoryTile extends TileMultipart with ISidedInventory
{
var invList = new LinkedList[IInventory]()
var slotMap = Array[(IInventory, Int)]()
override def copyFrom(that:TileMultipart)
{
super.copyFrom(that)
if(that.isInstanceOf[TIInventoryTile]) {
invList = that.asInstanceOf[TIInventoryTile].invList
rebuildSlotMap()
}
}
override def bindPart(part:TMultiPart)
{
super.bindPart(part)
if(part.isInstanceOf[IInventory]) {
invList+=part.asInstanceOf[IInventory]
rebuildSlotMap()
}
}
override def partRemoved(part:TMultiPart, p:Int)
{
super.partRemoved(part, p)
if(part.isInstanceOf[IInventory]) {
invList-=part.asInstanceOf[IInventory]
rebuildSlotMap()
}
}
override def clearParts()
{
super.clearParts()
invList.clear()
slotMap = Array()
}
def rebuildSlotMap() {
slotMap = Array.ofDim(invList.map(_.getSizeInventory).sum)
var i = 0
for(inv <- invList; s <- 0 until inv.getSizeInventory) {
slotMap(i) = (inv, s)
i+=1
}
}
def getSizeInventory:Int = slotMap.length
def getStackInSlot(i:Int) = {
val (inv, slot) = slotMap(i)
inv.getStackInSlot(slot)
}
def decrStackSize(i:Int, j:Int) = {
val (inv, slot) = slotMap(i)
inv.decrStackSize(slot, j)
}
def getStackInSlotOnClosing(i:Int) = {
val (inv, slot) = slotMap(i)
inv.getStackInSlotOnClosing(slot)
}
def setInventorySlotContents(i:Int, itemstack:ItemStack) = {
val (inv, slot) = slotMap(i)
inv.setInventorySlotContents(slot, itemstack)
}
def getInventoryName = "Multipart Inventory"
def hasCustomInventoryName = false
def getInventoryStackLimit = 64
def isUseableByPlayer(entityplayer:EntityPlayer) = true
def openInventory(){}
def closeInventory(){}
def isItemValidForSlot(i:Int, itemstack:ItemStack) = {
val (inv, slot) = slotMap(i)
inv.isItemValidForSlot(slot, itemstack)
}
def getAccessibleSlotsFromSide(side:Int) = {
val buf = new ArrayBuffer[Int]()
var base = 0
for(inv <- invList) {
inv match {
case isided:ISidedInventory => buf++=isided.getAccessibleSlotsFromSide(side).map(_ + base)
case _ =>
}
base+=inv.getSizeInventory
}
buf.toArray
}
def canInsertItem(i:Int, itemstack:ItemStack, j:Int) = {
val (inv, slot) = slotMap(i)
inv match {
case isided:ISidedInventory => isided.canInsertItem(slot, itemstack, j)
case _ => true
}
}
def canExtractItem(i:Int, itemstack:ItemStack, j:Int) = {
val (inv, slot) = slotMap(i)
inv match {
case isided:ISidedInventory => isided.canExtractItem(slot, itemstack, j)
case _ => true
}
}
}
/**
* To handle obfuscation issues, this is registered as a java trait.
*/
class JInventoryTile extends TileMultipart with TIInventoryTile
{
}
@@ -1,34 +0,0 @@
package codechicken.multipart.scalatraits
import codechicken.multipart.TileMultipart
import codechicken.multipart.TMultiPart
import codechicken.multipart.JPartialOcclusion
import codechicken.multipart.PartialOcclusionTest
/**
* Implementation for the partial occlusion test.
*/
class TPartialOcclusionTile extends TileMultipart
{
override def occlusionTest(parts:Seq[TMultiPart], npart:TMultiPart):Boolean =
{
if(npart.isInstanceOf[JPartialOcclusion] && !partialOcclusionTest(parts:+npart))
return false
return super.occlusionTest(parts, npart)
}
def partialOcclusionTest(parts:Seq[TMultiPart]):Boolean =
{
val test = new PartialOcclusionTest(parts.length)
var i = 0
while(i < parts.length)
{
val part = parts(i)
if(part.isInstanceOf[JPartialOcclusion])
test.fill(i, part.asInstanceOf[JPartialOcclusion])
i+=1
}
return test()
}
}
@@ -1,17 +0,0 @@
package codechicken.multipart.scalatraits
import codechicken.multipart.TileMultipartClient
import codechicken.multipart.IRandomDisplayTick
import java.util.Random
/**
* Saves processor time looping on tiles that don't need it
*/
trait TRandomDisplayTickTile extends TileMultipartClient
{
override def randomDisplayTick(random:Random)
{
for(p@(_p: IRandomDisplayTick) <- partList.iterator)
p.randomDisplayTick(random)
}
}
@@ -1,87 +0,0 @@
package codechicken.multipart.scalatraits
import codechicken.multipart.TileMultipart
import codechicken.multipart.IRedstonePart
import codechicken.multipart.RedstoneInteractions._
import codechicken.multipart.PartMap._
import codechicken.lib.vec.Rotation._
import codechicken.multipart.IRedstoneTile
import codechicken.multipart.TFacePart
import codechicken.multipart.TEdgePart
/**
* Mixin trait implementation for IRedstonePart
* Provides and overrides various redstone functions.
*/
trait TRedstoneTile extends TileMultipart with IRedstoneTile
{
/**
* Returns the strong (indirect) power being emitted fr
*/
override def strongPowerLevel(side:Int):Int =
{
var max = 0
for(p@(_p: IRedstonePart) <- partList.iterator)
{
val l = p.strongPowerLevel(side)
if(l > max) max = l
}
return max
}
def openConnections(side:Int):Int =
{
var m = 0x10
var i = 0
while(i < 4)
{
if(redstoneConductionE(edgeBetween(side, rotateSide(side&6, i))))
m|=1<<i
i+=1
}
m&=redstoneConductionF(side)
return m
}
def redstoneConductionF(i:Int) = partMap(i) match {
case null => 0x1F
case p => p.asInstanceOf[TFacePart].redstoneConductionMap
}
def redstoneConductionE(i:Int) = partMap(i) match {
case null => true
case p => p.asInstanceOf[TEdgePart].conductsRedstone
}
override def weakPowerLevel(side:Int):Int =
weakPowerLevel(side, otherConnectionMask(getWorldObj, xCoord, yCoord, zCoord, side, true))
override def canConnectRedstone(side:Int):Boolean =
{
val vside = vanillaToSide(side)
return (getConnectionMask(vside) & otherConnectionMask(getWorldObj, xCoord, yCoord, zCoord, vside, false)) > 0
}
def getConnectionMask(side:Int):Int =
{
val mask = openConnections(side)
var res = 0
partList.foreach(p =>
res|=connectionMask(p, side)&mask)
return res
}
def weakPowerLevel(side:Int, mask:Int):Int =
{
val tmask = openConnections(side)&mask
var max = 0
partList.foreach(p =>
if((connectionMask(p, side)&tmask) > 0)
{
val l = p.asInstanceOf[IRedstonePart].weakPowerLevel(side)
if(l > max) max = l
})
return max
}
}
@@ -1,64 +0,0 @@
package codechicken.multipart.scalatraits
import codechicken.multipart.TileMultipart
import codechicken.multipart.TMultiPart
import codechicken.multipart.TSlottedPart
/**
* Mixin implementation for TSlottedPart.
* Puts parts into a slot array for quick access at the cost of memory consumption
*/
trait TSlottedTile extends TileMultipart
{
var v_partMap = new Array[TMultiPart](27)
override def copyFrom(that:TileMultipart)
{
super.copyFrom(that)
if(that.isInstanceOf[TSlottedTile])
v_partMap = that.asInstanceOf[TSlottedTile].v_partMap
}
override def partMap(slot:Int) = v_partMap(slot)
override def clearParts()
{
super.clearParts()
for(i <- 0 until v_partMap.length)
v_partMap(i) = null
}
override def partRemoved(part:TMultiPart, p:Int)
{
super.partRemoved(part, p)
if(part.isInstanceOf[TSlottedPart])
for(i <- 0 until 27)
if(partMap(i) == part)
v_partMap(i) = null
}
override def canAddPart(part:TMultiPart):Boolean =
{
if(part.isInstanceOf[TSlottedPart])
{
val slotMask = part.asInstanceOf[TSlottedPart].getSlotMask
for(i <- 0 until v_partMap.length)
if((slotMask&1<<i) != 0 && partMap(i) != null)
return false
}
return super.canAddPart(part)
}
override def bindPart(part:TMultiPart)
{
super.bindPart(part)
if(part.isInstanceOf[TSlottedPart])
{
val mask = part.asInstanceOf[TSlottedPart].getSlotMask
for(i <- 0 until 27)
if ((mask&1<<i) > 0)
v_partMap(i) = part
}
}
}
@@ -1,59 +0,0 @@
package codechicken.multipart.scalatraits
import codechicken.multipart.TMultiPart
import codechicken.multipart.INeighborTileChange
import codechicken.multipart.TileMultipart
import codechicken.lib.vec.BlockCoord
/**
* Mixin implementation for INeighborTileChange
*
* Reduces unnecessary computation
*/
trait TTileChangeTile extends TileMultipart {
var weakTileChanges = false
override def copyFrom(that:TileMultipart)
{
super.copyFrom(that)
if(that.isInstanceOf[TTileChangeTile])
weakTileChanges = that.asInstanceOf[TTileChangeTile].weakTileChanges
}
override def bindPart(part:TMultiPart)
{
super.bindPart(part)
if(part.isInstanceOf[INeighborTileChange])
weakTileChanges|=part.asInstanceOf[INeighborTileChange].weakTileChanges
}
override def clearParts()
{
super.clearParts()
weakTileChanges = false
}
override def partRemoved(part:TMultiPart, p:Int) {
super.partRemoved(part, p)
weakTileChanges = partList.exists(p => p.isInstanceOf[INeighborTileChange] && p.asInstanceOf[INeighborTileChange].weakTileChanges)
}
override def onNeighborTileChange(tileX:Int, tileY:Int, tileZ:Int)
{
super.onNeighborTileChange(tileX, tileY, tileZ)
val offset = new BlockCoord(tileX, tileY, tileZ).sub(xCoord, yCoord, zCoord)
val diff = offset.absSum
val side = offset.toSide
if(side < 0 || diff <= 0 || diff > 2)
return
val weak = diff == 2
operate{ p =>
if(p.isInstanceOf[INeighborTileChange])
p.asInstanceOf[INeighborTileChange].onNeighborTileChanged(side, weak)
}
}
override def getWeakChanges() = weakTileChanges
}
-6
View File
@@ -1,6 +0,0 @@
{
"repo": "http://files.minecraftforge.net/maven/codechicken/CodeChickenLib/${mc_version}-${ccl_version}/",
"file": "CodeChickenLib-${mc_version}-${ccl_version}-universal.jar",
"dev": "CodeChickenLib-${mc_version}-${ccl_version}-dev.jar",
"class": "codechicken.lib.asm.ASMHelper"
}
Binary file not shown.
Loaded 100 of 105 files, more files were not shown because too many files have changed in this diff. Show more