mirror of
https://github.com/MinecraftForge/MinecraftForge.git
synced 2026-11-14 09:05:17 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a67e147dc |
No files matched your search
@@ -1,7 +0,0 @@
|
||||
* text eol=lf
|
||||
*.bat text eol=crlf
|
||||
*.patch text
|
||||
*.png binary
|
||||
*.exe binary
|
||||
*.dll binary
|
||||
*.zip binary
|
||||
@@ -1,7 +0,0 @@
|
||||
*.DS_Store
|
||||
/logs/
|
||||
/*.pyc
|
||||
/forge-*/
|
||||
/patches-old/
|
||||
/mcp/
|
||||
/eclipse/
|
||||
@@ -1,3 +0,0 @@
|
||||
[submodule "fml"]
|
||||
path = fml
|
||||
url = https://github.com/MinecraftForge/FML.git
|
||||
@@ -1,6 +0,0 @@
|
||||
Manifest-Version: 1.0
|
||||
Ant-Version: Apache Ant 1.8.2
|
||||
Created-By: 1.7.0_09-b30 (Oracle Corporation)
|
||||
Main-Class: net.minecraft.server.MinecraftServer
|
||||
Class-Path: minecraft_server.jar
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
import os, os.path, sys
|
||||
from optparse import OptionParser
|
||||
|
||||
forge_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
mcp_dir = os.path.abspath('..')
|
||||
|
||||
from forge import build_forge_dev
|
||||
|
||||
def main():
|
||||
|
||||
parser = OptionParser()
|
||||
parser.add_option('-m', '--mcp-dir', action='store', dest='mcp_dir', help='MCP Path', default=None)
|
||||
parser.add_option('-b', '--build', action='store', dest='build', help='Build number', default=None)
|
||||
options, _ = parser.parse_args()
|
||||
|
||||
build_num = 0
|
||||
if not options.build is None:
|
||||
try:
|
||||
build_num = int(options.build)
|
||||
except:
|
||||
pass
|
||||
|
||||
fml_dir = os.path.join(forge_dir, 'fml')
|
||||
mcp_dir = os.path.join(forge_dir, 'mcp')
|
||||
|
||||
if not options.mcp_dir is None:
|
||||
mcp_dir = os.path.abspath(options.mcp_dir)
|
||||
elif os.path.isfile(os.path.join('..', 'runtime', 'commands.py')):
|
||||
mcp_dir = os.path.abspath('..')
|
||||
|
||||
sys.exit(build_forge_dev(mcp_dir, forge_dir, fml_dir, build_num))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,89 +0,0 @@
|
||||
import os, os.path, sys, re
|
||||
import json, urllib2
|
||||
from pprint import pprint
|
||||
from urlparse import urlparse
|
||||
import base64
|
||||
import optparse, ast
|
||||
|
||||
class PreemptiveBasicAuthHandler(urllib2.BaseHandler):
|
||||
def __init__(self, password_mgr=None):
|
||||
if password_mgr is None:
|
||||
password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
|
||||
self.passwd = password_mgr
|
||||
self.add_password = self.passwd.add_password
|
||||
|
||||
def http_request(self,req):
|
||||
uri = req.get_full_url()
|
||||
user, pw = self.passwd.find_user_password(None,uri)
|
||||
if pw is None: return req
|
||||
raw = "%s:%s" % (user, pw)
|
||||
auth = 'Basic %s' % base64.b64encode(raw).strip()
|
||||
req.add_unredirected_header('Authorization', auth)
|
||||
return req
|
||||
|
||||
def https_request(self,req):
|
||||
return self.http_request(req)
|
||||
|
||||
def getBuildInfo(url, current_version=None):
|
||||
handler = PreemptiveBasicAuthHandler()
|
||||
handler.add_password(None, 'jenkins.minecraftforge.net:81', 'console_script', 'fd584bffa72fcac12181f37f80001200')
|
||||
file = urllib2.build_opener(handler).open(url)
|
||||
data = file.read()
|
||||
file.close()
|
||||
data = ast.literal_eval(data)['allBuilds']
|
||||
data = sorted(data, key=lambda key: key['number'], reverse=False)
|
||||
|
||||
items = []
|
||||
output = []
|
||||
|
||||
for build in data:
|
||||
build['actions'] = filter(lambda act: act is not None, build['actions'])
|
||||
build['actions'] = filter(lambda act: 'text' in act, build['actions'])
|
||||
build['actions'] = filter(lambda act: not ' ' in act['text'], build['actions'])
|
||||
if len(build['actions']) == 0:
|
||||
build['version'] = current_version
|
||||
current_version = None
|
||||
else:
|
||||
build['version'] = build['actions'][0]['text']
|
||||
build['items'] = build['changeSet']['items']
|
||||
for item in build['items']:
|
||||
item['author'] = item['author']['fullName']
|
||||
if build['result'] != 'SUCCESS':
|
||||
items += build['items']
|
||||
else:
|
||||
build['items'] = items + build['items']
|
||||
items = []
|
||||
output += [build]
|
||||
build.pop('changeSet')
|
||||
build.pop('actions')
|
||||
return sorted(output, key=lambda key: key['number'], reverse=True)
|
||||
|
||||
def make_changelog(job_path, target_build, change_file, current_version=None):
|
||||
builds = getBuildInfo('%s/api/python?tree=allBuilds[result,number,actions[text],changeSet[items[author[fullName],comment]]]&pretty=true' % job_path, current_version)
|
||||
|
||||
log = [ "Changelog:" ]
|
||||
|
||||
for build in builds:
|
||||
if int(build['number']) > target_build: continue
|
||||
if len(build['items']) == 0: continue
|
||||
log.append('')
|
||||
if build['version'] is None:
|
||||
log.append('Build %s' % build['number'])
|
||||
else:
|
||||
log.append('Build %s' % build['version'])
|
||||
for change in build['items']:
|
||||
comments = filter(lambda cmt: len(cmt) > 0, change['comment'].split('\n'))
|
||||
if len(comments) > 1:
|
||||
log.append('\t' + change['author'])
|
||||
for comment in comments:
|
||||
log.append('\t\t' + comment)
|
||||
elif len(comments) == 1:
|
||||
log.append('\t%s: %s' % (change['author'], comments[0]))
|
||||
|
||||
file = open(change_file, 'wb')
|
||||
for line in log:
|
||||
file.write('%s\n' % line)
|
||||
file.close()
|
||||
|
||||
if __name__ == '__main__':
|
||||
make_changelog("http://jenkins.minecraftforge.net:81/job/minecraftforge/", 70000, 'changelog.txt', 'pinecone')
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 24 KiB |
@@ -1,946 +0,0 @@
|
||||
|
||||
package ibxm;
|
||||
|
||||
public class Channel {
|
||||
public int pattern_loop_row;
|
||||
|
||||
private Module module;
|
||||
private Instrument instrument;
|
||||
private Sample sample;
|
||||
private int[] global_volume, current_note;
|
||||
private boolean linear_periods, fast_volume_slides, key_on, silent;
|
||||
private int sample_idx, sample_frac, step, left_gain, right_gain;
|
||||
private int volume, panning, fine_tune, period, porta_period, key_add;
|
||||
private int tremolo_speed, tremolo_depth, tremolo_tick, tremolo_wave, tremolo_add;
|
||||
private int vibrato_speed, vibrato_depth, vibrato_tick, vibrato_wave, vibrato_add;
|
||||
private int volume_slide_param, portamento_param, retrig_param;
|
||||
private int volume_envelope_tick, panning_envelope_tick;
|
||||
private int effect_tick, trigger_tick, fade_out_volume, random_seed;
|
||||
|
||||
private int log_2_sampling_rate;
|
||||
private static final int LOG_2_29024 = LogTable.log_2( 29024 );
|
||||
private static final int LOG_2_8287 = LogTable.log_2( 8287 );
|
||||
private static final int LOG_2_8363 = LogTable.log_2( 8363 );
|
||||
private static final int LOG_2_1712 = LogTable.log_2( 1712 );
|
||||
|
||||
private static final int[] sine_table = new int[] {
|
||||
0, 24 , 49, 74, 97, 120, 141, 161, 180, 197, 212, 224, 235, 244, 250, 253,
|
||||
255, 253, 250, 244, 235, 224, 212, 197, 180, 161, 141, 120, 97, 74, 49, 24
|
||||
};
|
||||
|
||||
public Channel( Module mod, int sampling_rate, int[] global_vol ) {
|
||||
module = mod;
|
||||
global_volume = global_vol;
|
||||
linear_periods = module.linear_periods;
|
||||
fast_volume_slides = module.fast_volume_slides;
|
||||
current_note = new int[ 5 ];
|
||||
log_2_sampling_rate = LogTable.log_2( sampling_rate );
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
tremolo_speed = 0;
|
||||
tremolo_depth = 0;
|
||||
tremolo_wave = 0;
|
||||
vibrato_speed = 0;
|
||||
vibrato_depth = 0;
|
||||
vibrato_wave = 0;
|
||||
volume_slide_param = 0;
|
||||
portamento_param = 0;
|
||||
retrig_param = 0;
|
||||
random_seed = 0xABC123;
|
||||
instrument = module.get_instrument( 0 );
|
||||
row( 48, 256, 0, 0, 0 );
|
||||
}
|
||||
|
||||
public void resample( int[] mixing_buffer, int frame_offset, int frames, int quality ) {
|
||||
if( !silent ) {
|
||||
switch( quality ) {
|
||||
default:
|
||||
sample.resample_nearest( sample_idx, sample_frac, step, left_gain, right_gain, mixing_buffer, frame_offset, frames );
|
||||
break;
|
||||
case 1:
|
||||
sample.resample_linear( sample_idx, sample_frac, step, left_gain, right_gain, mixing_buffer, frame_offset, frames );
|
||||
break;
|
||||
case 2:
|
||||
sample.resample_sinc( sample_idx, sample_frac, step, left_gain, right_gain, mixing_buffer, frame_offset, frames );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void update_sample_idx( int samples ) {
|
||||
sample_frac += step * samples;
|
||||
sample_idx += sample_frac >> IBXM.FP_SHIFT;
|
||||
sample_frac &= IBXM.FP_MASK;
|
||||
}
|
||||
|
||||
public void set_volume( int vol ) {
|
||||
if( vol < 0 ) {
|
||||
vol = 0;
|
||||
}
|
||||
if( vol > 64 ) {
|
||||
vol = 64;
|
||||
}
|
||||
volume = vol;
|
||||
}
|
||||
|
||||
public void set_panning( int pan ) {
|
||||
if( pan < 0 ) {
|
||||
pan = 0;
|
||||
}
|
||||
if( pan > 255 ) {
|
||||
pan = 255;
|
||||
}
|
||||
panning = pan;
|
||||
}
|
||||
|
||||
public void row( int key, int inst_idx, int volume_column, int effect, int effect_param ) {
|
||||
effect = effect & 0xFF;
|
||||
if( effect >= 0x30 ) {
|
||||
/* Effects above 0x30 are internal.*/
|
||||
effect = 0;
|
||||
}
|
||||
if( effect == 0x00 && effect_param != 0 ) {
|
||||
/* Arpeggio.*/
|
||||
effect = 0x40;
|
||||
}
|
||||
if( effect == 0x0E ) {
|
||||
/* Renumber 0x0Ex effect command.*/
|
||||
effect = 0x30 + ( ( effect_param & 0xF0 ) >> 4 );
|
||||
effect_param = effect_param & 0x0F;
|
||||
}
|
||||
if( effect == 0x21 ) {
|
||||
/* Renumber 0x21x effect command.*/
|
||||
effect = 0x40 + ( ( effect_param & 0xF0 ) >> 4 );
|
||||
effect_param = effect_param & 0x0F;
|
||||
}
|
||||
current_note[ 0 ] = key;
|
||||
current_note[ 1 ] = inst_idx;
|
||||
current_note[ 2 ] = volume_column;
|
||||
current_note[ 3 ] = effect;
|
||||
current_note[ 4 ] = effect_param;
|
||||
effect_tick = 0;
|
||||
trigger_tick += 1;
|
||||
update_envelopes();
|
||||
key_add = 0;
|
||||
vibrato_add = 0;
|
||||
tremolo_add = 0;
|
||||
if( ! ( effect == 0x3D && effect_param > 0 ) ) {
|
||||
/* Not note delay.*/
|
||||
trigger( key, inst_idx, volume_column, effect );
|
||||
/* Handle volume column.*/
|
||||
switch( volume_column & 0xF0 ) {
|
||||
case 0x00:
|
||||
/* Do nothing.*/
|
||||
break;
|
||||
case 0x60:
|
||||
/* Volume slide down.*/
|
||||
break;
|
||||
case 0x70:
|
||||
/* Volume slide up.*/
|
||||
break;
|
||||
case 0x80:
|
||||
/* Fine volume slide down.*/
|
||||
set_volume( volume - ( volume_column & 0x0F ) );
|
||||
break;
|
||||
case 0x90:
|
||||
/* Fine volume slide up.*/
|
||||
set_volume( volume + ( volume_column & 0x0F ) );
|
||||
break;
|
||||
case 0xA0:
|
||||
/* Set vibrato speed.*/
|
||||
set_vibrato_speed( volume_column & 0x0F );
|
||||
break;
|
||||
case 0xB0:
|
||||
/* Vibrato.*/
|
||||
set_vibrato_depth( volume_column & 0x0F );
|
||||
vibrato();
|
||||
break;
|
||||
case 0xC0:
|
||||
/* Set panning.*/
|
||||
set_panning( ( volume_column & 0x0F ) << 4 );
|
||||
break;
|
||||
case 0xD0:
|
||||
/* Panning slide left.*/
|
||||
break;
|
||||
case 0xE0:
|
||||
/* Panning slide right.*/
|
||||
break;
|
||||
case 0xF0:
|
||||
/* Tone portamento.*/
|
||||
set_portamento_param( volume_column & 0x0F );
|
||||
break;
|
||||
default:
|
||||
/* Set volume.*/
|
||||
set_volume( volume_column - 0x10 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( instrument.vibrato_depth > 0 ) {
|
||||
auto_vibrato();
|
||||
}
|
||||
switch( effect ) {
|
||||
case 0x01:
|
||||
/* Portmento Up.*/
|
||||
set_portamento_param( effect_param );
|
||||
portamento_up();
|
||||
break;
|
||||
case 0x02:
|
||||
/* Portamento Down.*/
|
||||
set_portamento_param( effect_param );
|
||||
portamento_down();
|
||||
break;
|
||||
case 0x03:
|
||||
/* Tone Portamento.*/
|
||||
set_portamento_param( effect_param );
|
||||
break;
|
||||
case 0x04:
|
||||
/* Vibrato.*/
|
||||
set_vibrato_speed( ( effect_param & 0xF0 ) >> 4 );
|
||||
set_vibrato_depth( effect_param & 0x0F );
|
||||
vibrato();
|
||||
break;
|
||||
case 0x05:
|
||||
/* Tone Portamento + Volume Slide.*/
|
||||
set_volume_slide_param( effect_param );
|
||||
volume_slide();
|
||||
break;
|
||||
case 0x06:
|
||||
/* Vibrato + Volume Slide.*/
|
||||
set_volume_slide_param( effect_param );
|
||||
vibrato();
|
||||
volume_slide();
|
||||
break;
|
||||
case 0x07:
|
||||
/* Tremolo.*/
|
||||
set_tremolo_speed( ( effect_param & 0xF0 ) >> 4 );
|
||||
set_tremolo_depth( effect_param & 0x0F );
|
||||
tremolo();
|
||||
break;
|
||||
case 0x08:
|
||||
/* Set Panning.*/
|
||||
set_panning( effect_param );
|
||||
break;
|
||||
case 0x09:
|
||||
/* Set Sample Index.*/
|
||||
set_sample_index( effect_param << 8 );
|
||||
break;
|
||||
case 0x0A:
|
||||
/* Volume Slide.*/
|
||||
set_volume_slide_param( effect_param );
|
||||
volume_slide();
|
||||
break;
|
||||
case 0x0B:
|
||||
/* Pattern Jump.*/
|
||||
break;
|
||||
case 0x0C:
|
||||
/* Set volume.*/
|
||||
set_volume( effect_param );
|
||||
break;
|
||||
case 0x0D:
|
||||
/* Pattern Break.*/
|
||||
break;
|
||||
case 0x0E:
|
||||
/* Extended Commands (See 0x30-0x3F).*/
|
||||
break;
|
||||
case 0x0F:
|
||||
/* Set Speed/Tempo.*/
|
||||
break;
|
||||
case 0x10:
|
||||
/* Set Global Volume.*/
|
||||
set_global_volume( effect_param );
|
||||
break;
|
||||
case 0x11:
|
||||
/* global Volume Slide.*/
|
||||
set_volume_slide_param( effect_param );
|
||||
break;
|
||||
case 0x14:
|
||||
/* Key Off*/
|
||||
if( effect_param == 0 ) {
|
||||
key_on = false;
|
||||
}
|
||||
break;
|
||||
case 0x15:
|
||||
/* Set Envelope Tick.*/
|
||||
set_envelope_tick( effect_param );
|
||||
break;
|
||||
case 0x19:
|
||||
/* Panning Slide.*/
|
||||
set_volume_slide_param( effect_param );
|
||||
break;
|
||||
case 0x1B:
|
||||
/* Retrig + Volume Slide.*/
|
||||
set_retrig_param( effect_param );
|
||||
retrig_volume_slide();
|
||||
break;
|
||||
case 0x1D:
|
||||
/* Tremor.*/
|
||||
set_retrig_param( effect_param );
|
||||
tremor();
|
||||
break;
|
||||
case 0x24:
|
||||
/* S3M Fine Vibrato.*/
|
||||
set_vibrato_speed( ( effect_param & 0xF0 ) >> 4 );
|
||||
set_vibrato_depth( effect_param & 0x0F );
|
||||
fine_vibrato();
|
||||
break;
|
||||
case 0x25:
|
||||
/* S3M Set Speed.*/
|
||||
break;
|
||||
case 0x30:
|
||||
/* Amiga Set Filter.*/
|
||||
break;
|
||||
case 0x31:
|
||||
/* Fine Portamento Up.*/
|
||||
set_portamento_param( 0xF0 | effect_param );
|
||||
portamento_up();
|
||||
break;
|
||||
case 0x32:
|
||||
/* Fine Portamento Down.*/
|
||||
set_portamento_param( 0xF0 | effect_param );
|
||||
portamento_down();
|
||||
break;
|
||||
case 0x33:
|
||||
/* Set Glissando Mode.*/
|
||||
break;
|
||||
case 0x34:
|
||||
/* Set Vibrato Waveform.*/
|
||||
set_vibrato_wave( effect_param );
|
||||
break;
|
||||
case 0x35:
|
||||
/* Set Fine Tune.*/
|
||||
break;
|
||||
case 0x36:
|
||||
/* Pattern Loop.*/
|
||||
break;
|
||||
case 0x37:
|
||||
/* Set Tremolo Waveform.*/
|
||||
set_tremolo_wave( effect_param );
|
||||
break;
|
||||
case 0x38:
|
||||
/* Set Panning(Obsolete).*/
|
||||
break;
|
||||
case 0x39:
|
||||
/* Retrig.*/
|
||||
set_retrig_param( effect_param );
|
||||
break;
|
||||
case 0x3A:
|
||||
/* Fine Volume Slide Up.*/
|
||||
set_volume_slide_param( ( effect_param << 4 ) | 0x0F );
|
||||
volume_slide();
|
||||
break;
|
||||
case 0x3B:
|
||||
/* Fine Volume Slide Down.*/
|
||||
set_volume_slide_param( 0xF0 | effect_param );
|
||||
volume_slide();
|
||||
break;
|
||||
case 0x3C:
|
||||
/* Note Cut.*/
|
||||
if( effect_param == 0 ) {
|
||||
set_volume( 0 );
|
||||
}
|
||||
break;
|
||||
case 0x3D:
|
||||
/* Note Delay.*/
|
||||
break;
|
||||
case 0x3E:
|
||||
/* Pattern Delay.*/
|
||||
break;
|
||||
case 0x3F:
|
||||
/* Invert Loop.*/
|
||||
break;
|
||||
case 0x40:
|
||||
/* Arpeggio.*/
|
||||
break;
|
||||
case 0x41:
|
||||
/* Extra Fine Porta Up.*/
|
||||
set_portamento_param( 0xE0 | effect_param );
|
||||
portamento_up();
|
||||
break;
|
||||
case 0x42:
|
||||
/* Extra Fine Porta Down.*/
|
||||
set_portamento_param( 0xE0 | effect_param );
|
||||
portamento_down();
|
||||
break;
|
||||
}
|
||||
calculate_amplitude();
|
||||
calculate_frequency();
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
int volume_column, effect, effect_param;
|
||||
volume_column = current_note[ 2 ];
|
||||
effect = current_note[ 3 ];
|
||||
effect_param = current_note[ 4 ];
|
||||
effect_tick += 1;
|
||||
if( effect == 0x3D && effect_param == effect_tick ) {
|
||||
/* Note delay.*/
|
||||
row( current_note[ 0 ], current_note[ 1 ], volume_column, 0, 0 );
|
||||
} else {
|
||||
trigger_tick += 1;
|
||||
vibrato_tick += 1;
|
||||
tremolo_tick += 1;
|
||||
update_envelopes();
|
||||
key_add = 0;
|
||||
vibrato_add = 0;
|
||||
tremolo_add = 0;
|
||||
if( instrument.vibrato_depth > 0 ) {
|
||||
auto_vibrato();
|
||||
}
|
||||
switch( volume_column & 0xF0 ) {
|
||||
case 0x60:
|
||||
/* Volume Slide Down.*/
|
||||
set_volume( volume - ( volume_column & 0x0F ) );
|
||||
break;
|
||||
case 0x70:
|
||||
/* Volume Slide Up.*/
|
||||
set_volume( volume + ( volume_column & 0x0F ) );
|
||||
break;
|
||||
case 0xB0:
|
||||
/* Vibrato.*/
|
||||
vibrato();
|
||||
break;
|
||||
case 0xD0:
|
||||
/* Panning Slide Left.*/
|
||||
set_panning( panning - ( volume_column & 0x0F ) );
|
||||
break;
|
||||
case 0xE0:
|
||||
/* Panning Slide Right.*/
|
||||
set_panning( panning + ( volume_column & 0x0F ) );
|
||||
break;
|
||||
case 0xF0:
|
||||
/* Tone Portamento.*/
|
||||
tone_portamento();
|
||||
break;
|
||||
}
|
||||
switch( effect ) {
|
||||
case 0x01:
|
||||
/* Portamento Up.*/
|
||||
portamento_up();
|
||||
break;
|
||||
case 0x02:
|
||||
/* Portamento Down.*/
|
||||
portamento_down();
|
||||
break;
|
||||
case 0x03:
|
||||
/* Tone Portamento.*/
|
||||
tone_portamento();
|
||||
break;
|
||||
case 0x04:
|
||||
/* Vibrato.*/
|
||||
vibrato();
|
||||
break;
|
||||
case 0x05:
|
||||
/* Tone Portamento + Volume Slide.*/
|
||||
tone_portamento();
|
||||
volume_slide();
|
||||
break;
|
||||
case 0x06:
|
||||
/* Vibrato + Volume Slide */
|
||||
vibrato();
|
||||
volume_slide();
|
||||
break;
|
||||
case 0x07:
|
||||
/* Tremolo.*/
|
||||
tremolo();
|
||||
break;
|
||||
case 0x0A:
|
||||
/* Volume Slide.*/
|
||||
volume_slide();
|
||||
break;
|
||||
case 0x11:
|
||||
/* Global Volume Slide.*/
|
||||
global_volume_slide();
|
||||
break;
|
||||
case 0x14:
|
||||
/* Key off.*/
|
||||
if( effect_tick == effect_param ) {
|
||||
key_on = false;
|
||||
}
|
||||
break;
|
||||
case 0x19:
|
||||
/* Panning Slide.*/
|
||||
panning_slide();
|
||||
break;
|
||||
case 0x1B:
|
||||
/* Retrig + Volume Slide.*/
|
||||
retrig_volume_slide();
|
||||
break;
|
||||
case 0x1D:
|
||||
/* Tremor.*/
|
||||
tremor();
|
||||
break;
|
||||
case 0x24:
|
||||
/* S3M Fine Vibrato.*/
|
||||
fine_vibrato();
|
||||
break;
|
||||
case 0x39:
|
||||
/* Retrig.*/
|
||||
retrig_volume_slide();
|
||||
break;
|
||||
case 0x3C:
|
||||
/* Note Cut.*/
|
||||
if( effect_tick == effect_param ) {
|
||||
set_volume( 0 );
|
||||
}
|
||||
break;
|
||||
case 0x40:
|
||||
/* Arpeggio.*/
|
||||
switch( effect_tick % 3 ) {
|
||||
case 1:
|
||||
key_add = ( effect_param & 0xF0 ) >> 4;
|
||||
break;
|
||||
case 2:
|
||||
key_add = effect_param & 0x0F;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
calculate_amplitude();
|
||||
calculate_frequency();
|
||||
}
|
||||
|
||||
private void set_vibrato_speed( int speed ) {
|
||||
if( speed > 0 ) {
|
||||
vibrato_speed = speed;
|
||||
}
|
||||
}
|
||||
|
||||
private void set_vibrato_depth( int depth ) {
|
||||
if( depth > 0 ) {
|
||||
vibrato_depth = depth;
|
||||
}
|
||||
}
|
||||
|
||||
private void set_vibrato_wave( int wave ) {
|
||||
if( wave < 0 || wave > 7 ) {
|
||||
wave = 0;
|
||||
}
|
||||
vibrato_wave = wave;
|
||||
}
|
||||
|
||||
private void set_tremolo_speed( int speed ) {
|
||||
if( speed > 0 ) {
|
||||
tremolo_speed = speed;
|
||||
}
|
||||
}
|
||||
|
||||
private void set_tremolo_depth( int depth ) {
|
||||
if( depth > 0 ) {
|
||||
tremolo_depth = depth;
|
||||
}
|
||||
}
|
||||
|
||||
private void set_tremolo_wave( int wave ) {
|
||||
if( wave < 0 || wave > 7 ) {
|
||||
wave = 0;
|
||||
}
|
||||
tremolo_wave = wave;
|
||||
}
|
||||
|
||||
private void vibrato() {
|
||||
int vibrato_phase;
|
||||
vibrato_phase = vibrato_tick * vibrato_speed;
|
||||
vibrato_add += waveform( vibrato_phase, vibrato_wave ) * vibrato_depth >> 5;
|
||||
}
|
||||
|
||||
private void fine_vibrato() {
|
||||
int vibrato_phase;
|
||||
vibrato_phase = vibrato_tick * vibrato_speed;
|
||||
vibrato_add += waveform( vibrato_phase, vibrato_wave ) * vibrato_depth >> 7;
|
||||
}
|
||||
|
||||
private void tremolo() {
|
||||
int tremolo_phase;
|
||||
tremolo_phase = tremolo_tick * tremolo_speed;
|
||||
tremolo_add += waveform( tremolo_phase, tremolo_wave ) * tremolo_depth >> 6;
|
||||
}
|
||||
|
||||
private void set_portamento_param( int param ) {
|
||||
if( param != 0 ) {
|
||||
portamento_param = param;
|
||||
}
|
||||
}
|
||||
|
||||
private void tone_portamento() {
|
||||
int new_period;
|
||||
if( porta_period < period ) {
|
||||
new_period = period - ( portamento_param << 2 );
|
||||
if( new_period < porta_period ) {
|
||||
new_period = porta_period;
|
||||
}
|
||||
set_period( new_period );
|
||||
}
|
||||
if( porta_period > period ) {
|
||||
new_period = period + ( portamento_param << 2 );
|
||||
if( new_period > porta_period ) {
|
||||
new_period = porta_period;
|
||||
}
|
||||
set_period( new_period );
|
||||
}
|
||||
}
|
||||
|
||||
private void portamento_up() {
|
||||
if( ( portamento_param & 0xF0 ) == 0xE0 ) {
|
||||
/* Extra-fine porta.*/
|
||||
if( effect_tick == 0 ) {
|
||||
set_period( period - ( portamento_param & 0x0F ) );
|
||||
}
|
||||
} else if( ( portamento_param & 0xF0 ) == 0xF0 ) {
|
||||
/* Fine porta.*/
|
||||
if( effect_tick == 0 ) {
|
||||
set_period( period - ( ( portamento_param & 0x0F ) << 2 ) );
|
||||
}
|
||||
} else {
|
||||
/* Normal porta.*/
|
||||
if( effect_tick > 0 ) {
|
||||
set_period( period - ( portamento_param << 2 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void portamento_down() {
|
||||
if( ( portamento_param & 0xF0 ) == 0xE0 ) {
|
||||
/* Extra-fine porta.*/
|
||||
if( effect_tick == 0 ) {
|
||||
set_period( period + ( portamento_param & 0x0F ) );
|
||||
}
|
||||
} else if( ( portamento_param & 0xF0 ) == 0xF0 ) {
|
||||
/* Fine porta.*/
|
||||
if( effect_tick == 0 ) {
|
||||
set_period( period + ( ( portamento_param & 0x0F ) << 2 ) );
|
||||
}
|
||||
} else {
|
||||
/* Normal porta.*/
|
||||
if( effect_tick > 0 ) {
|
||||
set_period( period + ( portamento_param << 2 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void set_period( int p ) {
|
||||
if( p < 32 ) {
|
||||
p = 32;
|
||||
}
|
||||
if( p > 32768 ) {
|
||||
p = 32768;
|
||||
}
|
||||
period = p;
|
||||
}
|
||||
|
||||
private void set_global_volume( int vol ) {
|
||||
if( vol < 0 ) {
|
||||
vol = 0;
|
||||
}
|
||||
if( vol > 64 ) {
|
||||
vol = 64;
|
||||
}
|
||||
global_volume[ 0 ] = vol;
|
||||
}
|
||||
|
||||
private void set_volume_slide_param( int param ) {
|
||||
if( param != 0 ) {
|
||||
volume_slide_param = param;
|
||||
}
|
||||
}
|
||||
|
||||
private void global_volume_slide() {
|
||||
int up, down;
|
||||
up = ( volume_slide_param & 0xF0 ) >> 4;
|
||||
down = volume_slide_param & 0x0F;
|
||||
set_global_volume( global_volume[ 0 ] + up - down );
|
||||
}
|
||||
|
||||
private void volume_slide() {
|
||||
int up, down;
|
||||
up = ( volume_slide_param & 0xF0 ) >> 4;
|
||||
down = volume_slide_param & 0x0F;
|
||||
if( down == 0x0F && up > 0 ) {
|
||||
/* Fine slide up.*/
|
||||
if( effect_tick == 0 ) {
|
||||
set_volume( volume + up );
|
||||
}
|
||||
} else if( up == 0x0F && down > 0 ) {
|
||||
/* Fine slide down.*/
|
||||
if( effect_tick == 0 ) {
|
||||
set_volume( volume - down );
|
||||
}
|
||||
} else {
|
||||
/* Normal slide.*/
|
||||
if( effect_tick > 0 || fast_volume_slides ) {
|
||||
set_volume( volume + up - down );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void panning_slide() {
|
||||
int left, right;
|
||||
left = ( volume_slide_param & 0xF0 ) >> 4;
|
||||
right = volume_slide_param & 0x0F;
|
||||
set_panning( panning - left + right );
|
||||
}
|
||||
|
||||
private void set_retrig_param( int param ) {
|
||||
if( param != 0 ) {
|
||||
retrig_param = param;
|
||||
}
|
||||
}
|
||||
|
||||
private void tremor() {
|
||||
int on_ticks, cycle_length, cycle_index;
|
||||
on_ticks = ( ( retrig_param & 0xF0 ) >> 4 ) + 1;
|
||||
cycle_length = on_ticks + ( retrig_param & 0x0F ) + 1;
|
||||
cycle_index = trigger_tick % cycle_length;
|
||||
if( cycle_index >= on_ticks ) {
|
||||
tremolo_add = -64;
|
||||
}
|
||||
}
|
||||
|
||||
private void retrig_volume_slide() {
|
||||
int retrig_volume, retrig_tick;
|
||||
retrig_volume = ( retrig_param & 0xF0 ) >> 4;
|
||||
retrig_tick = retrig_param & 0x0F;
|
||||
if( retrig_tick > 0 && ( trigger_tick % retrig_tick ) == 0 ) {
|
||||
set_sample_index( 0 );
|
||||
switch( retrig_volume ) {
|
||||
case 0x01:
|
||||
set_volume( volume - 1 );
|
||||
break;
|
||||
case 0x02:
|
||||
set_volume( volume - 2 );
|
||||
break;
|
||||
case 0x03:
|
||||
set_volume( volume - 4 );
|
||||
break;
|
||||
case 0x04:
|
||||
set_volume( volume - 8 );
|
||||
break;
|
||||
case 0x05:
|
||||
set_volume( volume - 16 );
|
||||
break;
|
||||
case 0x06:
|
||||
set_volume( volume - volume / 3 );
|
||||
break;
|
||||
case 0x07:
|
||||
set_volume( volume / 2 );
|
||||
break;
|
||||
case 0x09:
|
||||
set_volume( volume + 1 );
|
||||
break;
|
||||
case 0x0A:
|
||||
set_volume( volume + 2 );
|
||||
break;
|
||||
case 0x0B:
|
||||
set_volume( volume + 4 );
|
||||
break;
|
||||
case 0x0C:
|
||||
set_volume( volume + 8 );
|
||||
break;
|
||||
case 0x0D:
|
||||
set_volume( volume + 16 );
|
||||
break;
|
||||
case 0x0E:
|
||||
set_volume( volume + volume / 2 );
|
||||
break;
|
||||
case 0x0F:
|
||||
set_volume( volume * 2 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void set_sample_index( int index ) {
|
||||
if( index < 0 ) {
|
||||
index = 0;
|
||||
}
|
||||
sample_idx = index;
|
||||
sample_frac = 0;
|
||||
}
|
||||
|
||||
private void set_envelope_tick( int tick ) {
|
||||
volume_envelope_tick = tick;
|
||||
panning_envelope_tick = tick;
|
||||
}
|
||||
|
||||
private void trigger( int key, int instrument_idx, int volume_column, int effect ) {
|
||||
if( instrument_idx > 0 ) {
|
||||
instrument = module.get_instrument( instrument_idx );
|
||||
sample = instrument.get_sample_from_key( key );
|
||||
set_volume( sample.volume );
|
||||
if( sample.set_panning ) {
|
||||
set_panning( sample.panning );
|
||||
}
|
||||
set_envelope_tick( 0 );
|
||||
fade_out_volume = 32768;
|
||||
key_on = true;
|
||||
}
|
||||
if( key > 0 ) {
|
||||
if( key < 97 ) {
|
||||
porta_period = key_to_period( key );
|
||||
if( effect != 0x03 && effect != 0x05 ) {
|
||||
if( ( volume_column & 0xF0 ) != 0xF0 ) {
|
||||
/* Not portamento.*/
|
||||
trigger_tick = 0;
|
||||
if( vibrato_wave < 4 ) {
|
||||
vibrato_tick = 0;
|
||||
}
|
||||
if( tremolo_wave < 4 ) {
|
||||
tremolo_tick = 0;
|
||||
}
|
||||
set_period( porta_period );
|
||||
set_sample_index( 0 );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Key off.*/
|
||||
key_on = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void update_envelopes() {
|
||||
Envelope envelope;
|
||||
if( instrument.volume_envelope_active ) {
|
||||
if( !key_on ) {
|
||||
fade_out_volume -= instrument.volume_fade_out & 0xFFFF;
|
||||
if( fade_out_volume < 0 ) {
|
||||
fade_out_volume = 0;
|
||||
}
|
||||
}
|
||||
envelope = instrument.get_volume_envelope();
|
||||
volume_envelope_tick = envelope.next_tick( volume_envelope_tick, key_on );
|
||||
}
|
||||
if( instrument.panning_envelope_active ) {
|
||||
envelope = instrument.get_panning_envelope();
|
||||
panning_envelope_tick = envelope.next_tick( panning_envelope_tick, key_on );
|
||||
}
|
||||
}
|
||||
|
||||
private void auto_vibrato() {
|
||||
int sweep, depth, rate;
|
||||
sweep = instrument.vibrato_sweep & 0xFF;
|
||||
depth = instrument.vibrato_depth & 0x0F;
|
||||
rate = instrument.vibrato_rate & 0x3F;
|
||||
if( trigger_tick < sweep ) {
|
||||
depth = depth * trigger_tick / sweep;
|
||||
}
|
||||
vibrato_add += waveform( trigger_tick * rate, 0 ) * depth >> 9;
|
||||
}
|
||||
|
||||
private int waveform( int phase, int wform ) {
|
||||
int amplitude;
|
||||
amplitude = 0;
|
||||
switch( wform & 0x3 ) {
|
||||
case 0:
|
||||
/* Sine. */
|
||||
if( ( phase & 0x20 ) == 0 ) {
|
||||
amplitude = sine_table[ phase & 0x1F ];
|
||||
} else {
|
||||
amplitude = -sine_table[ phase & 0x1F ];
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
/* Saw. */
|
||||
if( ( phase & 0x20 ) == 0 ) {
|
||||
amplitude = ( phase & 0x1F ) << 3;
|
||||
} else {
|
||||
amplitude = ( ( phase & 0x1F ) << 3 ) - 255;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
/* Square. */
|
||||
if( ( phase & 0x20 ) == 0 ) {
|
||||
amplitude = 255;
|
||||
} else {
|
||||
amplitude = -255;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
/* Random. */
|
||||
amplitude = ( random_seed >> 15 ) - 255;
|
||||
random_seed = ( random_seed * 65 + 17 ) & 0xFFFFFF;
|
||||
break;
|
||||
}
|
||||
return amplitude;
|
||||
}
|
||||
|
||||
private int key_to_period( int key ) {
|
||||
int octave, log_2_period, period_out;
|
||||
octave = ( key << IBXM.FP_SHIFT ) / 12 + sample.transpose;
|
||||
if( linear_periods ) {
|
||||
period_out = 7744 - ( octave * 768 >> IBXM.FP_SHIFT );
|
||||
} else {
|
||||
log_2_period = LOG_2_29024 - octave;
|
||||
period_out = LogTable.raise_2( log_2_period );
|
||||
period_out = period_out >> ( IBXM.FP_SHIFT - 1 );
|
||||
period_out = ( period_out >> 1 ) + ( period_out & 1 );
|
||||
}
|
||||
return period_out;
|
||||
}
|
||||
|
||||
private void calculate_amplitude() {
|
||||
int envelope_volume, tremolo_volume, amplitude;
|
||||
int envelope_panning, mixer_panning, panning_range;
|
||||
Envelope envelope;
|
||||
envelope_volume = 0;
|
||||
if( instrument.volume_envelope_active ) {
|
||||
envelope = instrument.get_volume_envelope();
|
||||
envelope_volume = envelope.calculate_ampl( volume_envelope_tick );
|
||||
} else {
|
||||
if( key_on ) {
|
||||
envelope_volume = 64;
|
||||
}
|
||||
}
|
||||
tremolo_volume = volume + tremolo_add;
|
||||
if( tremolo_volume < 0 ) {
|
||||
tremolo_volume = 0;
|
||||
}
|
||||
if( tremolo_volume > 64 ) {
|
||||
tremolo_volume = 64;
|
||||
}
|
||||
amplitude = tremolo_volume << IBXM.FP_SHIFT - 6;
|
||||
amplitude = amplitude * envelope_volume >> 6;
|
||||
amplitude = amplitude * fade_out_volume >> 15;
|
||||
amplitude = amplitude * global_volume[ 0 ] >> 6;
|
||||
amplitude = amplitude * module.channel_gain >> IBXM.FP_SHIFT;
|
||||
silent = sample.has_finished( sample_idx );
|
||||
if( amplitude <= 0 ) {
|
||||
silent = true;
|
||||
} else {
|
||||
envelope_panning = 32;
|
||||
if( instrument.panning_envelope_active ) {
|
||||
envelope = instrument.get_panning_envelope();
|
||||
envelope_panning = envelope.calculate_ampl( panning_envelope_tick );
|
||||
}
|
||||
mixer_panning = ( panning & 0xFF ) << IBXM.FP_SHIFT - 8;
|
||||
panning_range = IBXM.FP_ONE - mixer_panning;
|
||||
if( panning_range > mixer_panning ) {
|
||||
panning_range = mixer_panning;
|
||||
}
|
||||
mixer_panning = mixer_panning + ( panning_range * ( envelope_panning - 32 ) >> 5 );
|
||||
left_gain = amplitude * ( IBXM.FP_ONE - mixer_panning ) >> IBXM.FP_SHIFT;
|
||||
right_gain = amplitude * mixer_panning >> IBXM.FP_SHIFT;
|
||||
}
|
||||
}
|
||||
|
||||
private void calculate_frequency() {
|
||||
int vibrato_period, log_2_freq;
|
||||
vibrato_period = period + vibrato_add;
|
||||
if( vibrato_period < 32 ) {
|
||||
vibrato_period = 32;
|
||||
}
|
||||
if( vibrato_period > 32768 ) {
|
||||
vibrato_period = 32768;
|
||||
}
|
||||
if( linear_periods ) {
|
||||
log_2_freq = LOG_2_8363 + ( 4608 - vibrato_period << IBXM.FP_SHIFT ) / 768;
|
||||
} else {
|
||||
log_2_freq = module.pal ? LOG_2_8287 : LOG_2_8363;
|
||||
log_2_freq = log_2_freq + LOG_2_1712 - LogTable.log_2( vibrato_period );
|
||||
}
|
||||
log_2_freq += ( key_add << IBXM.FP_SHIFT ) / 12;
|
||||
step = LogTable.raise_2( log_2_freq - log_2_sampling_rate );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
|
||||
package ibxm;
|
||||
|
||||
public class Envelope {
|
||||
public boolean sustain, looped;
|
||||
private int sustain_tick, loop_start_tick, loop_end_tick;
|
||||
private int[] ticks, ampls;
|
||||
|
||||
public Envelope() {
|
||||
set_num_points( 1 );
|
||||
}
|
||||
|
||||
public void set_num_points( int num_points ) {
|
||||
int point;
|
||||
if( num_points <= 0 ) {
|
||||
num_points = 1;
|
||||
}
|
||||
ticks = new int[ num_points ];
|
||||
ampls = new int[ num_points ];
|
||||
set_point( 0, 0, 0, false );
|
||||
}
|
||||
|
||||
/* When you set a point, all subsequent points are reset. */
|
||||
public void set_point( int point, int tick, int ampl, boolean delta ) {
|
||||
if( point >= 0 && point < ticks.length ) {
|
||||
if( point == 0 ) {
|
||||
tick = 0;
|
||||
}
|
||||
if( point > 0 ) {
|
||||
if( delta ) tick += ticks[ point - 1 ];
|
||||
if( tick <= ticks[ point - 1 ] ) {
|
||||
System.out.println( "Envelope: Point not valid (" + tick + " <= " + ticks[ point - 1 ] + ")");
|
||||
tick = ticks[ point - 1 ] + 1;
|
||||
}
|
||||
}
|
||||
ticks[ point ] = tick;
|
||||
ampls[ point ] = ampl;
|
||||
point += 1;
|
||||
while( point < ticks.length ) {
|
||||
ticks[ point ] = ticks[ point - 1 ] + 1;
|
||||
ampls[ point ] = 0;
|
||||
point += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void set_sustain_point( int point ) {
|
||||
if( point < 0 ) {
|
||||
point = 0;
|
||||
}
|
||||
if( point >= ticks.length ) {
|
||||
point = ticks.length - 1;
|
||||
}
|
||||
sustain_tick = ticks[ point ];
|
||||
}
|
||||
|
||||
public void set_loop_points( int start, int end ) {
|
||||
if( start < 0 ) {
|
||||
start = 0;
|
||||
}
|
||||
if( start >= ticks.length ) {
|
||||
start = ticks.length - 1;
|
||||
}
|
||||
if( end < start || end >= ticks.length ) {
|
||||
end = start;
|
||||
}
|
||||
loop_start_tick = ticks[ start ];
|
||||
loop_end_tick = ticks[ end ];
|
||||
}
|
||||
|
||||
public int next_tick( int tick, boolean key_on ) {
|
||||
tick = tick + 1;
|
||||
if( looped && tick >= loop_end_tick ) {
|
||||
tick = loop_start_tick;
|
||||
}
|
||||
if( sustain && key_on && tick >= sustain_tick ) {
|
||||
tick = sustain_tick;
|
||||
}
|
||||
return tick;
|
||||
}
|
||||
|
||||
public int calculate_ampl( int tick ) {
|
||||
int idx, point, delta_t, delta_a, ampl;
|
||||
ampl = ampls[ ticks.length - 1 ];
|
||||
if( tick < ticks[ ticks.length - 1 ] ) {
|
||||
point = 0;
|
||||
for( idx = 1; idx < ticks.length; idx++ ) {
|
||||
if( ticks[ idx ] <= tick ) {
|
||||
point = idx;
|
||||
}
|
||||
}
|
||||
delta_t = ticks[ point + 1 ] - ticks[ point ];
|
||||
delta_a = ampls[ point + 1 ] - ampls[ point ];
|
||||
ampl = ( delta_a << IBXM.FP_SHIFT ) / delta_t;
|
||||
ampl = ampl * ( tick - ticks[ point ] ) >> IBXM.FP_SHIFT;
|
||||
ampl = ampl + ampls[ point ];
|
||||
}
|
||||
return ampl;
|
||||
}
|
||||
|
||||
public void dump() {
|
||||
int idx, tick;
|
||||
for( idx = 0; idx < ticks.length; idx++ ) {
|
||||
System.out.println( ticks[ idx ] + ", " + ampls[ idx ] );
|
||||
}
|
||||
for( tick = 0; tick < 222; tick++ ) {
|
||||
System.out.print( calculate_ampl( tick ) + ", " );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,256 +0,0 @@
|
||||
|
||||
package ibxm;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class FastTracker2 {
|
||||
public static boolean is_xm( byte[] header_60_bytes ) {
|
||||
String xm_identifier;
|
||||
xm_identifier = ascii_text( header_60_bytes, 0, 17 );
|
||||
return xm_identifier.equals( "Extended Module: " );
|
||||
}
|
||||
|
||||
public static Module load_xm( byte[] header_60_bytes, DataInput data_input ) throws IOException {
|
||||
int xm_version, song_header_length, sequence_length;
|
||||
int num_channels, num_patterns, num_instruments, xm_flags, idx;
|
||||
byte[] structure_header, song_header;
|
||||
boolean delta_env;
|
||||
String tracker_name;
|
||||
Instrument instrument;
|
||||
Module module;
|
||||
if( !is_xm( header_60_bytes ) ) {
|
||||
throw new IllegalArgumentException( "Not an XM file!" );
|
||||
}
|
||||
xm_version = unsigned_short_le( header_60_bytes, 58 );
|
||||
if( xm_version != 0x0104 ) {
|
||||
throw new IllegalArgumentException( "Sorry, XM version " + xm_version + " is not supported!" );
|
||||
}
|
||||
module = new Module();
|
||||
module.song_title = ascii_text( header_60_bytes, 17, 20 );
|
||||
tracker_name = ascii_text( header_60_bytes, 38, 20 );
|
||||
delta_env = tracker_name.startsWith( "DigiBooster Pro" );
|
||||
structure_header = new byte[ 4 ];
|
||||
data_input.readFully( structure_header );
|
||||
song_header_length = int_le( structure_header, 0 );
|
||||
song_header = new byte[ song_header_length ];
|
||||
data_input.readFully( song_header, 4, song_header_length - 4 );
|
||||
sequence_length = unsigned_short_le( song_header, 4 );
|
||||
module.restart_sequence_index = unsigned_short_le( song_header, 6 );
|
||||
num_channels = unsigned_short_le( song_header, 8 );
|
||||
num_patterns = unsigned_short_le( song_header, 10 );
|
||||
num_instruments = unsigned_short_le( song_header, 12 );
|
||||
xm_flags = unsigned_short_le( song_header, 14 );
|
||||
module.linear_periods = ( xm_flags & 0x1 ) == 0x1;
|
||||
module.global_volume = 64;
|
||||
module.channel_gain = IBXM.FP_ONE * 3 / 8;
|
||||
module.default_speed = unsigned_short_le( song_header, 16 );
|
||||
module.default_tempo = unsigned_short_le( song_header, 18 );
|
||||
module.set_num_channels( num_channels );
|
||||
for( idx = 0; idx < num_channels; idx++ ) {
|
||||
module.set_initial_panning( idx, 128 );
|
||||
}
|
||||
module.set_sequence_length( sequence_length );
|
||||
for( idx = 0; idx < sequence_length; idx++ ) {
|
||||
module.set_sequence( idx, song_header[ 20 + idx ] & 0xFF );
|
||||
}
|
||||
module.set_num_patterns( num_patterns );
|
||||
for( idx = 0; idx < num_patterns; idx++ ) {
|
||||
module.set_pattern( idx, read_xm_pattern( data_input, num_channels ) );
|
||||
}
|
||||
module.set_num_instruments( num_instruments );
|
||||
for( idx = 1; idx <= num_instruments; idx++ ) {
|
||||
try {
|
||||
instrument = read_xm_instrument( data_input, delta_env );
|
||||
module.set_instrument( idx, instrument );
|
||||
} catch( EOFException e ) {
|
||||
System.out.println( "Instrument " + idx + " is missing!" );
|
||||
}
|
||||
}
|
||||
return module;
|
||||
}
|
||||
|
||||
private static Pattern read_xm_pattern( DataInput data_input, int num_channels ) throws IOException {
|
||||
int pattern_header_length, packing_type, num_rows, pattern_data_length;
|
||||
byte[] structure_header, pattern_header, pattern_data;
|
||||
Pattern pattern;
|
||||
structure_header = new byte[ 4 ];
|
||||
data_input.readFully( structure_header );
|
||||
pattern_header_length = int_le( structure_header, 0 );
|
||||
pattern_header = new byte[ pattern_header_length ];
|
||||
data_input.readFully( pattern_header, 4, pattern_header_length - 4 );
|
||||
packing_type = pattern_header[ 4 ];
|
||||
if( packing_type != 0 ) {
|
||||
throw new IllegalArgumentException( "Pattern packing type " + packing_type + " is not supported!" );
|
||||
}
|
||||
pattern = new Pattern();
|
||||
pattern.num_rows = unsigned_short_le( pattern_header, 5 );
|
||||
pattern_data_length = unsigned_short_le( pattern_header, 7 );
|
||||
pattern_data = new byte[ pattern_data_length ];
|
||||
data_input.readFully( pattern_data );
|
||||
pattern.set_pattern_data( pattern_data );
|
||||
return pattern;
|
||||
}
|
||||
|
||||
private static Instrument read_xm_instrument( DataInput data_input, boolean delta_env ) throws IOException {
|
||||
int instrument_header_length, num_samples, idx;
|
||||
int env_tick, env_ampl, env_num_points, flags;
|
||||
byte[] structure_header, instrument_header, sample_headers;
|
||||
Instrument instrument;
|
||||
Envelope envelope;
|
||||
structure_header = new byte[ 4 ];
|
||||
data_input.readFully( structure_header );
|
||||
instrument_header_length = int_le( structure_header, 0 );
|
||||
instrument_header = new byte[ instrument_header_length ];
|
||||
data_input.readFully( instrument_header, 4, instrument_header_length - 4 );
|
||||
instrument = new Instrument();
|
||||
instrument.name = ascii_text( instrument_header, 4, 22 );
|
||||
num_samples = unsigned_short_le( instrument_header, 27 );
|
||||
if( num_samples > 0 ) {
|
||||
instrument.set_num_samples( num_samples );
|
||||
for( idx = 0; idx < 96; idx++ ) {
|
||||
instrument.set_key_to_sample( idx + 1, instrument_header[ 33 + idx ] & 0xFF );
|
||||
}
|
||||
envelope = new Envelope();
|
||||
env_num_points = instrument_header[ 225 ] & 0xFF;
|
||||
envelope.set_num_points( env_num_points );
|
||||
for( idx = 0; idx < env_num_points; idx++ ) {
|
||||
env_tick = unsigned_short_le( instrument_header, 129 + idx * 4 );
|
||||
env_ampl = unsigned_short_le( instrument_header, 131 + idx * 4 );
|
||||
envelope.set_point( idx, env_tick, env_ampl, delta_env );
|
||||
}
|
||||
envelope.set_sustain_point( instrument_header[ 227 ] & 0xFF );
|
||||
envelope.set_loop_points( instrument_header[ 228 ] & 0xFF, instrument_header[ 229 ] & 0xFF );
|
||||
flags = instrument_header[ 233 ] & 0xFF;
|
||||
instrument.volume_envelope_active = ( flags & 0x1 ) == 0x1;
|
||||
envelope.sustain = ( flags & 0x2 ) == 0x2;
|
||||
envelope.looped = ( flags & 0x4 ) == 0x4;
|
||||
instrument.set_volume_envelope( envelope );
|
||||
envelope = new Envelope();
|
||||
env_num_points = instrument_header[ 226 ] & 0xFF;
|
||||
envelope.set_num_points( env_num_points );
|
||||
for( idx = 0; idx < env_num_points; idx++ ) {
|
||||
env_tick = unsigned_short_le( instrument_header, 177 + idx * 4 );
|
||||
env_ampl = unsigned_short_le( instrument_header, 179 + idx * 4 );
|
||||
envelope.set_point( idx, env_tick, env_ampl, delta_env );
|
||||
}
|
||||
envelope.set_sustain_point( instrument_header[ 230 ] & 0xFF );
|
||||
envelope.set_loop_points( instrument_header[ 231 ] & 0xFF, instrument_header[ 232 ] & 0xFF );
|
||||
flags = instrument_header[ 234 ] & 0xFF;
|
||||
instrument.panning_envelope_active = ( flags & 0x1 ) == 0x1;
|
||||
envelope.sustain = ( flags & 0x2 ) == 0x2;
|
||||
envelope.looped = ( flags & 0x4 ) == 0x4;
|
||||
instrument.set_panning_envelope( envelope );
|
||||
instrument.vibrato_type = instrument_header[ 235 ] & 0xFF;
|
||||
instrument.vibrato_sweep = instrument_header[ 236 ] & 0xFF;
|
||||
instrument.vibrato_depth = instrument_header[ 237 ] & 0xFF;
|
||||
instrument.vibrato_rate = instrument_header[ 238 ] & 0xFF;
|
||||
instrument.volume_fade_out = unsigned_short_le( instrument_header, 239 );
|
||||
sample_headers = new byte[ num_samples * 40 ];
|
||||
data_input.readFully( sample_headers );
|
||||
for( idx = 0; idx < num_samples; idx++ ) {
|
||||
instrument.set_sample( idx, read_xm_sample( sample_headers, idx, data_input ) );
|
||||
}
|
||||
}
|
||||
return instrument;
|
||||
}
|
||||
|
||||
private static Sample read_xm_sample( byte[] sample_headers, int sample_idx, DataInput data_input ) throws IOException {
|
||||
int header_offset, sample_length, loop_start, loop_length;
|
||||
int flags, in_idx, out_idx, sam, last_sam;
|
||||
int fine_tune, relative_note;
|
||||
boolean sixteen_bit, ping_pong;
|
||||
byte[] raw_sample_data;
|
||||
short[] decoded_sample_data;
|
||||
Sample sample;
|
||||
header_offset = sample_idx * 40;
|
||||
sample = new Sample();
|
||||
sample_length = int_le( sample_headers, header_offset );
|
||||
loop_start = int_le( sample_headers, header_offset + 4 );
|
||||
loop_length = int_le( sample_headers, header_offset + 8 );
|
||||
sample.volume = sample_headers[ header_offset + 12 ] & 0xFF;
|
||||
fine_tune = sample_headers[ header_offset + 13 ];
|
||||
fine_tune = ( fine_tune << IBXM.FP_SHIFT ) / 1536;
|
||||
sample.set_panning = true;
|
||||
flags = sample_headers[ header_offset + 14 ] & 0xFF;
|
||||
if( ( flags & 0x03 ) == 0 ) {
|
||||
loop_length = 0;
|
||||
}
|
||||
ping_pong = ( flags & 0x02 ) == 0x02;
|
||||
sixteen_bit = ( flags & 0x10 ) == 0x10;
|
||||
sample.panning = sample_headers[ header_offset + 15 ] & 0xFF;
|
||||
relative_note = sample_headers[ header_offset + 16 ];
|
||||
relative_note = ( relative_note << IBXM.FP_SHIFT ) / 12;
|
||||
sample.transpose = relative_note + fine_tune;
|
||||
sample.name = ascii_text( sample_headers, header_offset + 18, 22 );
|
||||
raw_sample_data = new byte[ sample_length ];
|
||||
try {
|
||||
data_input.readFully( raw_sample_data );
|
||||
} catch( EOFException e ) {
|
||||
System.out.println( "Sample has been truncated!" );
|
||||
}
|
||||
in_idx = 0;
|
||||
out_idx = 0;
|
||||
sam = 0;
|
||||
last_sam = 0;
|
||||
if( sixteen_bit ) {
|
||||
decoded_sample_data = new short[ sample_length >> 1 ];
|
||||
while( in_idx < raw_sample_data.length ) {
|
||||
sam = raw_sample_data[ in_idx ] & 0xFF;
|
||||
sam = sam | ( ( raw_sample_data[ in_idx + 1 ] & 0xFF ) << 8 );
|
||||
last_sam = last_sam + sam;
|
||||
decoded_sample_data[ out_idx ] = ( short ) last_sam;
|
||||
in_idx += 2;
|
||||
out_idx += 1;
|
||||
}
|
||||
sample.set_sample_data( decoded_sample_data, loop_start >> 1, loop_length >> 1, ping_pong );
|
||||
} else {
|
||||
decoded_sample_data = new short[ sample_length ];
|
||||
while( in_idx < raw_sample_data.length ) {
|
||||
sam = raw_sample_data[ in_idx ] & 0xFF;
|
||||
last_sam = last_sam + sam;
|
||||
decoded_sample_data[ out_idx ] = ( short ) ( last_sam << 8 );
|
||||
in_idx += 1;
|
||||
out_idx += 1;
|
||||
}
|
||||
sample.set_sample_data( decoded_sample_data, loop_start, loop_length, ping_pong );
|
||||
}
|
||||
return sample;
|
||||
}
|
||||
|
||||
private static int unsigned_short_le( byte[] buffer, int offset ) {
|
||||
int value;
|
||||
value = buffer[ offset ] & 0xFF;
|
||||
value = value | ( ( buffer[ offset + 1 ] & 0xFF ) << 8 );
|
||||
return value;
|
||||
}
|
||||
|
||||
private static int int_le( byte[] buffer, int offset ) {
|
||||
int value;
|
||||
value = buffer[ offset ] & 0xFF;
|
||||
value = value | ( ( buffer[ offset + 1 ] & 0xFF ) << 8 );
|
||||
value = value | ( ( buffer[ offset + 2 ] & 0xFF ) << 16 );
|
||||
value = value | ( ( buffer[ offset + 3 ] & 0x7F ) << 24 );
|
||||
return value;
|
||||
}
|
||||
|
||||
private static String ascii_text( byte[] buffer, int offset, int length ) {
|
||||
int idx, chr;
|
||||
byte[] string_buffer;
|
||||
String string;
|
||||
string_buffer = new byte[ length ];
|
||||
for( idx = 0; idx < length; idx++ ) {
|
||||
chr = buffer[ offset + idx ];
|
||||
if( chr < 32 ) {
|
||||
chr = 32;
|
||||
}
|
||||
string_buffer[ idx ] = ( byte ) chr;
|
||||
}
|
||||
try {
|
||||
string = new String( string_buffer, 0, length, "ISO-8859-1" );
|
||||
} catch( UnsupportedEncodingException e ) {
|
||||
string = "";
|
||||
}
|
||||
return string;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,343 +0,0 @@
|
||||
|
||||
package ibxm;
|
||||
|
||||
public class IBXM {
|
||||
public static final String VERSION = "ibxm alpha 51 (c)2008 [email protected]";
|
||||
|
||||
public static final int FP_SHIFT = 15;
|
||||
public static final int FP_ONE = 1 << FP_SHIFT;
|
||||
public static final int FP_MASK = FP_ONE - 1;
|
||||
|
||||
private int sampling_rate, resampling_quality, volume_ramp_length;
|
||||
private int tick_length_samples, current_tick_samples;
|
||||
private int[] mixing_buffer, volume_ramp_buffer;
|
||||
|
||||
private Module module;
|
||||
private Channel[] channels;
|
||||
private int[] global_volume, note;
|
||||
private int current_sequence_index, next_sequence_index;
|
||||
private int current_row, next_row;
|
||||
private int tick_counter, ticks_per_row;
|
||||
private int pattern_loop_count, pattern_loop_channel;
|
||||
|
||||
public IBXM( int sample_rate ) {
|
||||
|
||||
/** MODIFIED 13 Oct 2009 by Paul Lamb **/
|
||||
// System.out.println( VERSION );
|
||||
/***************************************/
|
||||
|
||||
if( sample_rate < 8000 ) {
|
||||
sample_rate = 8000;
|
||||
}
|
||||
sampling_rate = sample_rate;
|
||||
volume_ramp_length = sampling_rate >> 10;
|
||||
volume_ramp_buffer = new int[ volume_ramp_length * 2 ];
|
||||
mixing_buffer = new int[ sampling_rate / 6 ];
|
||||
global_volume = new int[ 1 ];
|
||||
note = new int[ 5 ];
|
||||
set_module( new Module() );
|
||||
set_resampling_quality( 1 );
|
||||
}
|
||||
|
||||
public void set_module( Module m ) {
|
||||
int channel_idx;
|
||||
module = m;
|
||||
channels = new Channel[ module.get_num_channels() ];
|
||||
for( channel_idx = 0; channel_idx < channels.length; channel_idx++ ) {
|
||||
channels[ channel_idx ] = new Channel( module, sampling_rate, global_volume );
|
||||
}
|
||||
set_sequence_index( 0, 0 );
|
||||
}
|
||||
|
||||
public void set_resampling_quality( int quality ) {
|
||||
resampling_quality = quality;
|
||||
}
|
||||
|
||||
public int calculate_song_duration() {
|
||||
int song_duration;
|
||||
set_sequence_index( 0, 0 );
|
||||
next_tick();
|
||||
song_duration = tick_length_samples;
|
||||
while( !next_tick() ) {
|
||||
song_duration += tick_length_samples;
|
||||
}
|
||||
set_sequence_index( 0, 0 );
|
||||
return song_duration;
|
||||
}
|
||||
|
||||
public void set_sequence_index( int sequence_index, int row ) {
|
||||
int channel_idx;
|
||||
global_volume[ 0 ] = 64;
|
||||
for( channel_idx = 0; channel_idx < channels.length; channel_idx++ ) {
|
||||
channels[ channel_idx ].reset();
|
||||
channels[ channel_idx ].set_panning( module.get_initial_panning( channel_idx ) );
|
||||
}
|
||||
set_global_volume( module.global_volume );
|
||||
set_speed( 6 );
|
||||
set_speed( module.default_speed );
|
||||
set_tempo( 125 );
|
||||
set_tempo( module.default_tempo );
|
||||
pattern_loop_count = -1;
|
||||
next_sequence_index = sequence_index;
|
||||
next_row = row;
|
||||
tick_counter = 0;
|
||||
current_tick_samples = tick_length_samples;
|
||||
clear_vol_ramp_buffer();
|
||||
}
|
||||
|
||||
public void seek( int sample_position ) {
|
||||
int idx;
|
||||
set_sequence_index( 0, 0 );
|
||||
next_tick();
|
||||
while( sample_position > tick_length_samples ) {
|
||||
sample_position -= tick_length_samples;
|
||||
next_tick();
|
||||
}
|
||||
mix_tick();
|
||||
current_tick_samples = sample_position;
|
||||
}
|
||||
|
||||
public void get_audio( byte[] output_buffer, int frames ) {
|
||||
int output_idx, mix_idx, mix_end, count, amplitude;
|
||||
output_idx = 0;
|
||||
while( frames > 0 ) {
|
||||
count = tick_length_samples - current_tick_samples;
|
||||
if( count > frames ) {
|
||||
count = frames;
|
||||
}
|
||||
mix_idx = current_tick_samples << 1;
|
||||
mix_end = mix_idx + ( count << 1 ) - 1;
|
||||
while( mix_idx <= mix_end ) {
|
||||
amplitude = mixing_buffer[ mix_idx ];
|
||||
if( amplitude > 32767 ) {
|
||||
amplitude = 32767;
|
||||
}
|
||||
if( amplitude < -32768 ) {
|
||||
amplitude = -32768;
|
||||
}
|
||||
output_buffer[ output_idx ] = ( byte ) ( amplitude >> 8 );
|
||||
output_buffer[ output_idx + 1 ] = ( byte ) ( amplitude & 0xFF );
|
||||
output_idx += 2;
|
||||
mix_idx += 1;
|
||||
}
|
||||
current_tick_samples = mix_idx >> 1;
|
||||
frames -= count;
|
||||
if( frames > 0 ) {
|
||||
next_tick();
|
||||
mix_tick();
|
||||
current_tick_samples = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void mix_tick() {
|
||||
int channel_idx, mix_idx, mix_len;
|
||||
mix_idx = 0;
|
||||
mix_len = tick_length_samples + volume_ramp_length << 1;
|
||||
while( mix_idx < mix_len ) {
|
||||
mixing_buffer[ mix_idx ] = 0;
|
||||
mix_idx += 1;
|
||||
}
|
||||
for( channel_idx = 0; channel_idx < channels.length; channel_idx++ ) {
|
||||
mix_len = tick_length_samples + volume_ramp_length;
|
||||
channels[ channel_idx ].resample( mixing_buffer, 0, mix_len, resampling_quality );
|
||||
}
|
||||
volume_ramp();
|
||||
}
|
||||
|
||||
private boolean next_tick() {
|
||||
int channel_idx;
|
||||
boolean song_end;
|
||||
for( channel_idx = 0; channel_idx < channels.length; channel_idx++ ) {
|
||||
channels[ channel_idx ].update_sample_idx( tick_length_samples );
|
||||
}
|
||||
tick_counter -= 1;
|
||||
if( tick_counter <= 0 ) {
|
||||
tick_counter = ticks_per_row;
|
||||
song_end = next_row();
|
||||
} else {
|
||||
for( channel_idx = 0; channel_idx < channels.length; channel_idx++ ) {
|
||||
channels[ channel_idx ].tick();
|
||||
}
|
||||
song_end = false;
|
||||
}
|
||||
return song_end;
|
||||
}
|
||||
|
||||
private boolean next_row() {
|
||||
int channel_idx, effect, effect_param;
|
||||
boolean song_end;
|
||||
Pattern pattern;
|
||||
song_end = false;
|
||||
if( next_sequence_index < 0 ) {
|
||||
/* Bad next sequence index.*/
|
||||
next_sequence_index = 0;
|
||||
next_row = 0;
|
||||
}
|
||||
if( next_sequence_index >= module.get_sequence_length() ) {
|
||||
/* End of sequence.*/
|
||||
song_end = true;
|
||||
next_sequence_index = module.restart_sequence_index;
|
||||
if( next_sequence_index < 0 ) {
|
||||
next_sequence_index = 0;
|
||||
}
|
||||
if( next_sequence_index >= module.get_sequence_length() ) {
|
||||
next_sequence_index = 0;
|
||||
}
|
||||
next_row = 0;
|
||||
}
|
||||
if( next_sequence_index < current_sequence_index ) {
|
||||
/* Jump to previous pattern. */
|
||||
song_end = true;
|
||||
}
|
||||
if( next_sequence_index == current_sequence_index ) {
|
||||
if( next_row <= current_row ) {
|
||||
if( pattern_loop_count < 0 ) {
|
||||
/* Jump to previous row in the same pattern, but not a pattern loop. */
|
||||
song_end = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
current_sequence_index = next_sequence_index;
|
||||
pattern = module.get_pattern_from_sequence( current_sequence_index );
|
||||
if( next_row < 0 || next_row >= pattern.num_rows ) {
|
||||
/* Bad next row.*/
|
||||
next_row = 0;
|
||||
}
|
||||
current_row = next_row;
|
||||
next_row = current_row + 1;
|
||||
if( next_row >= pattern.num_rows ) {
|
||||
next_sequence_index = current_sequence_index + 1;
|
||||
next_row = 0;
|
||||
}
|
||||
for( channel_idx = 0; channel_idx < channels.length; channel_idx++ ) {
|
||||
pattern.get_note( note, current_row * channels.length + channel_idx );
|
||||
effect = note[ 3 ];
|
||||
effect_param = note[ 4 ];
|
||||
channels[ channel_idx ].row( note[ 0 ], note[ 1 ], note[ 2 ], effect, effect_param );
|
||||
switch( effect ) {
|
||||
case 0x0B:
|
||||
/* Pattern Jump.*/
|
||||
if( pattern_loop_count < 0 ) {
|
||||
next_sequence_index = effect_param;
|
||||
next_row = 0;
|
||||
}
|
||||
break;
|
||||
case 0x0D:
|
||||
/* Pattern Break.*/
|
||||
if( pattern_loop_count < 0 ) {
|
||||
next_sequence_index = current_sequence_index + 1;
|
||||
next_row = ( effect_param >> 4 ) * 10 + ( effect_param & 0x0F );
|
||||
}
|
||||
break;
|
||||
case 0x0E:
|
||||
/* Extended.*/
|
||||
switch( effect_param & 0xF0 ) {
|
||||
case 0x60:
|
||||
/* Pattern loop.*/
|
||||
if( ( effect_param & 0x0F ) == 0 ) {
|
||||
/* Set loop marker on this channel. */
|
||||
channels[ channel_idx ].pattern_loop_row = current_row;
|
||||
}
|
||||
if( channels[ channel_idx ].pattern_loop_row < current_row ) {
|
||||
/* Marker and parameter are valid. Begin looping. */
|
||||
if( pattern_loop_count < 0 ) {
|
||||
/* Not already looping, begin. */
|
||||
pattern_loop_count = effect_param & 0x0F;
|
||||
pattern_loop_channel = channel_idx;
|
||||
}
|
||||
if( pattern_loop_channel == channel_idx ) {
|
||||
/* Loop in progress on this channel. Next iteration. */
|
||||
if( pattern_loop_count == 0 ) {
|
||||
/* Loop finished. */
|
||||
/* Invalidate current marker. */
|
||||
channels[ channel_idx ].pattern_loop_row = current_row + 1;
|
||||
} else {
|
||||
/* Count must be higher than zero. */
|
||||
/* Loop and cancel any breaks on this row. */
|
||||
next_row = channels[ channel_idx ].pattern_loop_row;
|
||||
next_sequence_index = current_sequence_index;
|
||||
}
|
||||
pattern_loop_count -= 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0xE0:
|
||||
/* Pattern delay.*/
|
||||
tick_counter += ticks_per_row * ( effect_param & 0x0F );
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0x0F:
|
||||
/* Set Speed/Tempo.*/
|
||||
if( effect_param < 32 ) {
|
||||
set_speed( effect_param );
|
||||
tick_counter = ticks_per_row;
|
||||
} else {
|
||||
set_tempo( effect_param );
|
||||
}
|
||||
break;
|
||||
case 0x25:
|
||||
/* S3M Set Speed.*/
|
||||
set_speed( effect_param );
|
||||
tick_counter = ticks_per_row;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return song_end;
|
||||
}
|
||||
|
||||
private void set_global_volume( int volume ) {
|
||||
if( volume < 0 ) {
|
||||
volume = 0;
|
||||
}
|
||||
if( volume > 64 ) {
|
||||
volume = 64;
|
||||
}
|
||||
global_volume[ 0 ] = volume;
|
||||
}
|
||||
|
||||
private void set_speed( int speed ) {
|
||||
if( speed > 0 && speed < 256 ) {
|
||||
ticks_per_row = speed;
|
||||
}
|
||||
}
|
||||
|
||||
private void set_tempo( int bpm ) {
|
||||
if( bpm > 31 && bpm < 256 ) {
|
||||
tick_length_samples = ( sampling_rate * 5 ) / ( bpm * 2 );
|
||||
}
|
||||
}
|
||||
|
||||
private void volume_ramp() {
|
||||
int ramp_idx, next_idx, ramp_end;
|
||||
int volume_ramp_delta, volume, sample;
|
||||
sample = 0;
|
||||
volume_ramp_delta = FP_ONE / volume_ramp_length;
|
||||
volume = 0;
|
||||
ramp_idx = 0;
|
||||
next_idx = 2 * tick_length_samples;
|
||||
ramp_end = volume_ramp_length * 2 - 1;
|
||||
while( ramp_idx <= ramp_end ) {
|
||||
sample = volume_ramp_buffer[ ramp_idx ] * ( FP_ONE - volume ) >> FP_SHIFT;
|
||||
mixing_buffer[ ramp_idx ] = sample + ( mixing_buffer[ ramp_idx ] * volume >> FP_SHIFT );
|
||||
volume_ramp_buffer[ ramp_idx ] = mixing_buffer[ next_idx + ramp_idx ];
|
||||
sample = volume_ramp_buffer[ ramp_idx + 1 ] * ( FP_ONE - volume ) >> FP_SHIFT;
|
||||
mixing_buffer[ ramp_idx + 1 ] = sample + ( mixing_buffer[ ramp_idx + 1 ] * volume >> FP_SHIFT );
|
||||
volume_ramp_buffer[ ramp_idx + 1 ] = mixing_buffer[ next_idx + ramp_idx + 1 ];
|
||||
volume += volume_ramp_delta;
|
||||
ramp_idx += 2;
|
||||
}
|
||||
}
|
||||
|
||||
private void clear_vol_ramp_buffer() {
|
||||
int ramp_idx, ramp_end;
|
||||
ramp_idx = 0;
|
||||
ramp_end = volume_ramp_length * 2 - 1;
|
||||
while( ramp_idx <= ramp_end ) {
|
||||
volume_ramp_buffer[ ramp_idx ] = 0;
|
||||
ramp_idx += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
|
||||
package ibxm;
|
||||
|
||||
public class Instrument {
|
||||
public String name;
|
||||
public int vibrato_type, vibrato_sweep;
|
||||
public int vibrato_depth, vibrato_rate;
|
||||
public boolean volume_envelope_active, panning_envelope_active;
|
||||
public int volume_fade_out;
|
||||
|
||||
private Envelope volume_envelope, panning_envelope;
|
||||
private int[] key_to_sample;
|
||||
private Sample[] samples;
|
||||
|
||||
public Instrument() {
|
||||
name = "";
|
||||
set_volume_envelope( new Envelope() );
|
||||
set_panning_envelope( new Envelope() );
|
||||
key_to_sample = new int[ 96 ];
|
||||
set_num_samples( 1 );
|
||||
}
|
||||
|
||||
public Envelope get_volume_envelope() {
|
||||
return volume_envelope;
|
||||
}
|
||||
|
||||
public void set_volume_envelope( Envelope envelope ) {
|
||||
if( envelope != null ) {
|
||||
volume_envelope = envelope;
|
||||
}
|
||||
}
|
||||
|
||||
public Envelope get_panning_envelope() {
|
||||
return panning_envelope;
|
||||
}
|
||||
|
||||
public void set_panning_envelope( Envelope envelope ) {
|
||||
if( envelope != null ) {
|
||||
panning_envelope = envelope;
|
||||
}
|
||||
}
|
||||
|
||||
public Sample get_sample_from_key( int key ) {
|
||||
int sample_idx;
|
||||
sample_idx = 0;
|
||||
if( key > 0 && key <= key_to_sample.length ) {
|
||||
sample_idx = key_to_sample[ key - 1 ];
|
||||
}
|
||||
return get_sample( sample_idx );
|
||||
}
|
||||
|
||||
public void set_key_to_sample( int key, int sample ) {
|
||||
if( key > 0 && key <= key_to_sample.length ) {
|
||||
key_to_sample[ key - 1 ] = sample;
|
||||
}
|
||||
}
|
||||
|
||||
public int get_num_samples() {
|
||||
return samples.length;
|
||||
}
|
||||
|
||||
public void set_num_samples( int num_samples ) {
|
||||
if( num_samples < 1 ) {
|
||||
num_samples = 1;
|
||||
}
|
||||
samples = new Sample[ num_samples ];
|
||||
set_sample( 0, null );
|
||||
}
|
||||
|
||||
public Sample get_sample( int sample_index ) {
|
||||
Sample sample;
|
||||
sample = null;
|
||||
if( sample_index >= 0 && sample_index < samples.length ) {
|
||||
sample = samples[ sample_index ];
|
||||
}
|
||||
if( sample == null ) {
|
||||
sample = samples[ 0 ];
|
||||
}
|
||||
return sample;
|
||||
}
|
||||
|
||||
public void set_sample( int sample_index, Sample sample ) {
|
||||
if( sample_index >= 0 && sample_index < samples.length ) {
|
||||
samples[ sample_index ] = sample;
|
||||
}
|
||||
if( samples[ 0 ] == null ) {
|
||||
samples[ 0 ] = new Sample();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
|
||||
package ibxm;
|
||||
|
||||
/*
|
||||
Base-2 Log and Exp functions, using linear-interpolated tables.
|
||||
*/
|
||||
public class LogTable {
|
||||
private static final int TABLE_SHIFT = 7; // 128 points (+1 for interp)
|
||||
private static final int INTERP_SHIFT = IBXM.FP_SHIFT - TABLE_SHIFT;
|
||||
private static final int INTERP_MASK = ( 1 << INTERP_SHIFT ) - 1;
|
||||
|
||||
private static final int[] exp_2_table = {
|
||||
32768, 32945, 33124, 33304, 33485, 33667, 33850, 34033,
|
||||
34218, 34404, 34591, 34779, 34968, 35157, 35348, 35540,
|
||||
35733, 35927, 36122, 36319, 36516, 36714, 36913, 37114,
|
||||
37315, 37518, 37722, 37926, 38132, 38339, 38548, 38757,
|
||||
38967, 39179, 39392, 39606, 39821, 40037, 40254, 40473,
|
||||
40693, 40914, 41136, 41359, 41584, 41810, 42037, 42265,
|
||||
42494, 42725, 42957, 43190, 43425, 43661, 43898, 44136,
|
||||
44376, 44617, 44859, 45103, 45347, 45594, 45841, 46090,
|
||||
46340, 46592, 46845, 47099, 47355, 47612, 47871, 48131,
|
||||
48392, 48655, 48919, 49185, 49452, 49720, 49990, 50262,
|
||||
50535, 50809, 51085, 51362, 51641, 51922, 52204, 52487,
|
||||
52772, 53059, 53347, 53636, 53928, 54220, 54515, 54811,
|
||||
55108, 55408, 55709, 56011, 56315, 56621, 56928, 57238,
|
||||
57548, 57861, 58175, 58491, 58809, 59128, 59449, 59772,
|
||||
60096, 60423, 60751, 61081, 61412, 61746, 62081, 62418,
|
||||
62757, 63098, 63440, 63785, 64131, 64479, 64830, 65182,
|
||||
65536
|
||||
};
|
||||
|
||||
private static final int[] log_2_table = {
|
||||
0, 367, 732, 1095, 1454, 1811, 2165, 2517,
|
||||
2865, 3212, 3556, 3897, 4236, 4572, 4906, 5238,
|
||||
5568, 5895, 6220, 6542, 6863, 7181, 7497, 7812,
|
||||
8124, 8434, 8742, 9048, 9352, 9654, 9954, 10252,
|
||||
10548, 10843, 11136, 11427, 11716, 12003, 12289, 12573,
|
||||
12855, 13136, 13414, 13692, 13967, 14241, 14514, 14785,
|
||||
15054, 15322, 15588, 15853, 16117, 16378, 16639, 16898,
|
||||
17156, 17412, 17667, 17920, 18172, 18423, 18673, 18921,
|
||||
19168, 19413, 19657, 19900, 20142, 20383, 20622, 20860,
|
||||
21097, 21333, 21568, 21801, 22034, 22265, 22495, 22724,
|
||||
22952, 23178, 23404, 23628, 23852, 24074, 24296, 24516,
|
||||
24736, 24954, 25171, 25388, 25603, 25817, 26031, 26243,
|
||||
26455, 26665, 26875, 27084, 27292, 27499, 27705, 27910,
|
||||
28114, 28317, 28520, 28721, 28922, 29122, 29321, 29519,
|
||||
29716, 29913, 30109, 30304, 30498, 30691, 30884, 31076,
|
||||
31267, 31457, 31646, 31835, 32023, 32210, 32397, 32582,
|
||||
32768
|
||||
};
|
||||
|
||||
/*
|
||||
Calculate log-base-2 of x (non-fixed-point).
|
||||
A fixed point value is returned.
|
||||
*/
|
||||
public static int log_2( int x ) {
|
||||
int shift;
|
||||
/* Scale x to range 1.0 <= x < 2.0 */
|
||||
shift = IBXM.FP_SHIFT;
|
||||
while( x < IBXM.FP_ONE ) {
|
||||
x <<= 1;
|
||||
shift--;
|
||||
}
|
||||
while( x >= ( IBXM.FP_ONE << 1 ) ) {
|
||||
x >>= 1;
|
||||
shift++;
|
||||
}
|
||||
return ( IBXM.FP_ONE * shift ) + eval_table( log_2_table, x - IBXM.FP_ONE );
|
||||
}
|
||||
|
||||
/*
|
||||
Raise 2 to the power x (fixed point).
|
||||
A fixed point value is returned.
|
||||
*/
|
||||
public static int raise_2( int x ) {
|
||||
int y;
|
||||
y = eval_table( exp_2_table, x & IBXM.FP_MASK ) << IBXM.FP_SHIFT;
|
||||
return y >> IBXM.FP_SHIFT - ( x >> IBXM.FP_SHIFT );
|
||||
}
|
||||
|
||||
private static int eval_table( int[] table, int x ) {
|
||||
int table_idx, table_frac, c, m, y;
|
||||
table_idx = x >> INTERP_SHIFT;
|
||||
table_frac = x & INTERP_MASK;
|
||||
c = table[ table_idx ];
|
||||
m = table[ table_idx + 1 ] - c;
|
||||
y = ( m * table_frac >> INTERP_SHIFT ) + c;
|
||||
return y >> 15 - IBXM.FP_SHIFT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
|
||||
package ibxm;
|
||||
|
||||
public class Module {
|
||||
public String song_title;
|
||||
public boolean linear_periods, fast_volume_slides, pal;
|
||||
public int global_volume, channel_gain;
|
||||
public int default_speed, default_tempo;
|
||||
public int restart_sequence_index;
|
||||
|
||||
private int[] initial_panning, sequence;
|
||||
private Pattern[] patterns;
|
||||
private Instrument[] instruments;
|
||||
|
||||
private Pattern default_pattern;
|
||||
private Instrument default_instrument;
|
||||
|
||||
public Module() {
|
||||
song_title = IBXM.VERSION;
|
||||
set_num_channels( 1 );
|
||||
set_sequence_length( 1 );
|
||||
set_num_patterns( 0 );
|
||||
set_num_instruments( 0 );
|
||||
default_pattern = new Pattern();
|
||||
default_instrument = new Instrument();
|
||||
}
|
||||
|
||||
public int get_num_channels() {
|
||||
return initial_panning.length;
|
||||
}
|
||||
|
||||
public void set_num_channels( int num_channels ) {
|
||||
if( num_channels < 1 ) {
|
||||
num_channels = 1;
|
||||
}
|
||||
initial_panning = new int[ num_channels ];
|
||||
}
|
||||
|
||||
public int get_initial_panning( int channel ) {
|
||||
int panning;
|
||||
panning = 128;
|
||||
if( channel >= 0 && channel < initial_panning.length ) {
|
||||
panning = initial_panning[ channel ];
|
||||
}
|
||||
return panning;
|
||||
}
|
||||
|
||||
public void set_initial_panning( int channel, int panning ) {
|
||||
if( channel >= 0 && channel < initial_panning.length ) {
|
||||
initial_panning[ channel ] = panning;
|
||||
}
|
||||
}
|
||||
|
||||
public int get_sequence_length() {
|
||||
return sequence.length;
|
||||
}
|
||||
|
||||
public void set_sequence_length( int sequence_length ) {
|
||||
if( sequence_length < 0 ) {
|
||||
sequence_length = 0;
|
||||
}
|
||||
sequence = new int[ sequence_length ];
|
||||
}
|
||||
|
||||
public void set_sequence( int sequence_index, int pattern_index ) {
|
||||
if( sequence_index >= 0 && sequence_index < sequence.length ) {
|
||||
sequence[ sequence_index ] = pattern_index;
|
||||
}
|
||||
}
|
||||
|
||||
public int get_num_patterns() {
|
||||
return patterns.length;
|
||||
}
|
||||
|
||||
public void set_num_patterns( int num_patterns ) {
|
||||
if( num_patterns < 0 ) {
|
||||
num_patterns = 0;
|
||||
}
|
||||
patterns = new Pattern[ num_patterns ];
|
||||
}
|
||||
|
||||
public Pattern get_pattern_from_sequence( int sequence_index ) {
|
||||
Pattern pattern;
|
||||
pattern = default_pattern;
|
||||
if( sequence_index >= 0 && sequence_index < sequence.length ) {
|
||||
pattern = get_pattern( sequence[ sequence_index ] );
|
||||
}
|
||||
return pattern;
|
||||
}
|
||||
|
||||
public Pattern get_pattern( int pattern_index ) {
|
||||
Pattern pattern;
|
||||
pattern = null;
|
||||
if( pattern_index >= 0 && pattern_index < patterns.length ) {
|
||||
pattern = patterns[ pattern_index ];
|
||||
}
|
||||
if( pattern == null ) {
|
||||
pattern = default_pattern;
|
||||
}
|
||||
return pattern;
|
||||
}
|
||||
|
||||
public void set_pattern( int pattern_index, Pattern pattern ) {
|
||||
if( pattern_index >= 0 && pattern_index < patterns.length ) {
|
||||
patterns[ pattern_index ] = pattern;
|
||||
}
|
||||
}
|
||||
|
||||
public int get_num_instruments() {
|
||||
return instruments.length;
|
||||
}
|
||||
|
||||
public void set_num_instruments( int num_instruments ) {
|
||||
if( num_instruments < 0 ) {
|
||||
num_instruments = 0;
|
||||
}
|
||||
instruments = new Instrument[ num_instruments ];
|
||||
}
|
||||
|
||||
public Instrument get_instrument( int instrument_index ) {
|
||||
Instrument instrument;
|
||||
instrument = null;
|
||||
if( instrument_index > 0 && instrument_index <= instruments.length ) {
|
||||
instrument = instruments[ instrument_index - 1 ];
|
||||
}
|
||||
if( instrument == null ) {
|
||||
instrument = default_instrument;
|
||||
}
|
||||
return instrument;
|
||||
}
|
||||
|
||||
public void set_instrument( int instrument_index, Instrument instrument ) {
|
||||
if( instrument_index > 0 && instrument_index <= instruments.length ) {
|
||||
instruments[ instrument_index - 1 ] = instrument;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
|
||||
package ibxm;
|
||||
|
||||
public class Pattern {
|
||||
public int num_rows;
|
||||
|
||||
private int data_offset, note_index;
|
||||
private byte[] pattern_data;
|
||||
|
||||
public Pattern() {
|
||||
num_rows = 1;
|
||||
set_pattern_data( new byte[ 0 ] );
|
||||
}
|
||||
|
||||
public void set_pattern_data( byte[] data ) {
|
||||
if( data != null ) {
|
||||
pattern_data = data;
|
||||
}
|
||||
data_offset = 0;
|
||||
note_index = 0;
|
||||
}
|
||||
|
||||
public void get_note( int[] note, int index ) {
|
||||
if( index < note_index ) {
|
||||
note_index = 0;
|
||||
data_offset = 0;
|
||||
}
|
||||
while( note_index <= index ) {
|
||||
data_offset = next_note( data_offset, note );
|
||||
note_index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
public int next_note( int data_offset, int[] note ) {
|
||||
int bitmask, field;
|
||||
if( data_offset < 0 ) {
|
||||
data_offset = pattern_data.length;
|
||||
}
|
||||
bitmask = 0x80;
|
||||
if( data_offset < pattern_data.length ) {
|
||||
bitmask = pattern_data[ data_offset ] & 0xFF;
|
||||
}
|
||||
if( ( bitmask & 0x80 ) == 0x80 ) {
|
||||
data_offset += 1;
|
||||
} else {
|
||||
bitmask = 0x1F;
|
||||
}
|
||||
for( field = 0; field < 5; field++ ) {
|
||||
note[ field ] = 0;
|
||||
if( ( bitmask & 0x01 ) == 0x01 ) {
|
||||
if( data_offset < pattern_data.length ) {
|
||||
note[ field ] = pattern_data[ data_offset ] & 0xFF;
|
||||
data_offset += 1;
|
||||
}
|
||||
}
|
||||
bitmask = bitmask >> 1;
|
||||
}
|
||||
return data_offset;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
|
||||
package ibxm;
|
||||
|
||||
import java.io.*;
|
||||
import javax.sound.sampled.*;
|
||||
|
||||
public class Player {
|
||||
private Thread play_thread;
|
||||
private IBXM ibxm;
|
||||
private Module module;
|
||||
private int song_duration, play_position;
|
||||
private boolean running, loop;
|
||||
private byte[] output_buffer;
|
||||
private SourceDataLine output_line;
|
||||
|
||||
/**
|
||||
Simple command-line test player.
|
||||
*/
|
||||
public static void main( String[] args ) throws Exception {
|
||||
if( args.length < 1 ) {
|
||||
System.err.println( "Usage: java ibxm.Player <module file>" );
|
||||
System.exit( 0 );
|
||||
}
|
||||
FileInputStream file_input_stream = new FileInputStream( args[ 0 ] );
|
||||
Player player = new Player();
|
||||
player.set_module( Player.load_module( file_input_stream ) );
|
||||
file_input_stream.close();
|
||||
player.play();
|
||||
}
|
||||
|
||||
/**
|
||||
Decode the data in the specified InputStream into a Module instance.
|
||||
@param input an InputStream containing the module file to be decoded.
|
||||
@throws IllegalArgumentException if the data is not recognised as a module file.
|
||||
*/
|
||||
public static Module load_module( InputStream input ) throws IllegalArgumentException, IOException {
|
||||
DataInputStream data_input_stream = new DataInputStream( input );
|
||||
/* Check if data is in XM format.*/
|
||||
byte[] xm_header = new byte[ 60 ];
|
||||
data_input_stream.readFully( xm_header );
|
||||
if( FastTracker2.is_xm( xm_header ) )
|
||||
return FastTracker2.load_xm( xm_header, data_input_stream );
|
||||
/* Check if data is in ScreamTracker 3 format.*/
|
||||
byte[] s3m_header = new byte[ 96 ];
|
||||
System.arraycopy( xm_header, 0, s3m_header, 0, 60 );
|
||||
data_input_stream.readFully( s3m_header, 60, 36 );
|
||||
if( ScreamTracker3.is_s3m( s3m_header ) )
|
||||
return ScreamTracker3.load_s3m( s3m_header, data_input_stream );
|
||||
/* Check if data is in ProTracker format.*/
|
||||
byte[] mod_header = new byte[ 1084 ];
|
||||
System.arraycopy( s3m_header, 0, mod_header, 0, 96 );
|
||||
data_input_stream.readFully( mod_header, 96, 988 );
|
||||
return ProTracker.load_mod( mod_header, data_input_stream );
|
||||
}
|
||||
|
||||
/**
|
||||
Instantiate a new Player.
|
||||
*/
|
||||
public Player() throws LineUnavailableException {
|
||||
ibxm = new IBXM( 48000 );
|
||||
set_loop( true );
|
||||
output_line = AudioSystem.getSourceDataLine( new AudioFormat( 48000, 16, 2, true, true ) );
|
||||
output_buffer = new byte[ 1024 * 4 ];
|
||||
}
|
||||
|
||||
/**
|
||||
Set the Module instance to be played.
|
||||
*/
|
||||
public void set_module( Module m ) {
|
||||
if( m != null ) module = m;
|
||||
stop();
|
||||
ibxm.set_module( module );
|
||||
song_duration = ibxm.calculate_song_duration();
|
||||
}
|
||||
|
||||
/**
|
||||
If loop is true, playback will continue indefinitely,
|
||||
otherwise the module will play through once and stop.
|
||||
*/
|
||||
public void set_loop( boolean loop ) {
|
||||
this.loop = loop;
|
||||
}
|
||||
|
||||
/**
|
||||
Open the audio device and begin playback.
|
||||
If a module is already playing it will be restarted.
|
||||
*/
|
||||
public void play() {
|
||||
stop();
|
||||
play_thread = new Thread( new Driver() );
|
||||
play_thread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
Stop playback and close the audio device.
|
||||
*/
|
||||
public void stop() {
|
||||
running = false;
|
||||
if( play_thread != null ) {
|
||||
try {
|
||||
play_thread.join();
|
||||
} catch( InterruptedException ie ) {}
|
||||
}
|
||||
}
|
||||
|
||||
private class Driver implements Runnable {
|
||||
public void run() {
|
||||
if( running ) return;
|
||||
try {
|
||||
output_line.open();
|
||||
output_line.start();
|
||||
play_position = 0;
|
||||
running = true;
|
||||
while( running ) {
|
||||
int frames = song_duration - play_position;
|
||||
if( frames > 1024 ) frames = 1024;
|
||||
ibxm.get_audio( output_buffer, frames );
|
||||
output_line.write( output_buffer, 0, frames * 4 );
|
||||
play_position += frames;
|
||||
if( play_position >= song_duration ) {
|
||||
play_position = 0;
|
||||
if( !loop ) running = false;
|
||||
}
|
||||
}
|
||||
output_line.drain();
|
||||
output_line.close();
|
||||
} catch( LineUnavailableException lue ) {
|
||||
lue.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,234 +0,0 @@
|
||||
package ibxm;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class ProTracker {
|
||||
public static boolean is_mod( byte[] header_1084_bytes ) {
|
||||
boolean is_mod;
|
||||
is_mod = false;
|
||||
if( calculate_num_channels( header_1084_bytes ) > 0 ) {
|
||||
is_mod = true;
|
||||
}
|
||||
return is_mod;
|
||||
}
|
||||
|
||||
public static Module load_mod( byte[] header_1084_bytes, DataInput data_input ) throws IOException {
|
||||
int num_channels, channel_idx, panning;
|
||||
int sequence_length, restart_idx, sequence_idx;
|
||||
int num_patterns, pattern_idx, instrument_idx;
|
||||
Module module;
|
||||
num_channels = calculate_num_channels( header_1084_bytes );
|
||||
if( num_channels < 1 ) {
|
||||
throw new IllegalArgumentException( "ProTracker: Unrecognised module format!" );
|
||||
}
|
||||
module = new Module();
|
||||
module.song_title = ascii_text( header_1084_bytes, 0, 20 );
|
||||
module.pal = ( num_channels == 4 );
|
||||
module.global_volume = 64;
|
||||
module.channel_gain = IBXM.FP_ONE * 3 / 8;
|
||||
module.default_speed = 6;
|
||||
module.default_tempo = 125;
|
||||
module.set_num_channels( num_channels );
|
||||
for( channel_idx = 0; channel_idx < num_channels; channel_idx++ ) {
|
||||
panning = 64;
|
||||
if( ( channel_idx & 0x03 ) == 0x01 || ( channel_idx & 0x03 ) == 0x02 ) {
|
||||
panning = 192;
|
||||
}
|
||||
module.set_initial_panning( channel_idx, panning );
|
||||
}
|
||||
sequence_length = header_1084_bytes[ 950 ] & 0x7F;
|
||||
restart_idx = header_1084_bytes[ 951 ] & 0x7F;
|
||||
if( restart_idx >= sequence_length ) {
|
||||
restart_idx = 0;
|
||||
}
|
||||
module.restart_sequence_index = restart_idx;
|
||||
module.set_sequence_length( sequence_length );
|
||||
for( sequence_idx = 0; sequence_idx < sequence_length; sequence_idx++ ) {
|
||||
module.set_sequence( sequence_idx, header_1084_bytes[ 952 + sequence_idx ] & 0x7F );
|
||||
}
|
||||
num_patterns = calculate_num_patterns( header_1084_bytes );
|
||||
module.set_num_patterns( num_patterns );
|
||||
for( pattern_idx = 0; pattern_idx < num_patterns; pattern_idx++ ) {
|
||||
module.set_pattern( pattern_idx, read_mod_pattern( data_input, num_channels ) );
|
||||
}
|
||||
module.set_num_instruments( 31 );
|
||||
for( instrument_idx = 1; instrument_idx <= 31; instrument_idx++ ) {
|
||||
module.set_instrument( instrument_idx, read_mod_instrument( header_1084_bytes, instrument_idx, data_input ) );
|
||||
}
|
||||
return module;
|
||||
}
|
||||
|
||||
private static int calculate_num_patterns( byte[] module_header ) {
|
||||
int num_patterns, order_entry, pattern_idx;
|
||||
num_patterns = 0;
|
||||
for( pattern_idx = 0; pattern_idx < 128; pattern_idx++ ) {
|
||||
order_entry = module_header[ 952 + pattern_idx ] & 0x7F;
|
||||
if( order_entry >= num_patterns ) {
|
||||
num_patterns = order_entry + 1;
|
||||
}
|
||||
}
|
||||
return num_patterns;
|
||||
}
|
||||
|
||||
private static int calculate_num_channels( byte[] module_header ) {
|
||||
int num_channels;
|
||||
switch( ( module_header[ 1082 ] << 8 ) | module_header[ 1083 ] ) {
|
||||
case 0x4b2e: /* M.K. */
|
||||
case 0x4b21: /* M!K! */
|
||||
case 0x542e: /* N.T. */
|
||||
case 0x5434: /* FLT4 */
|
||||
num_channels = 4;
|
||||
break;
|
||||
case 0x484e: /* xCHN */
|
||||
num_channels = module_header[ 1080 ] - 48;
|
||||
break;
|
||||
case 0x4348: /* xxCH */
|
||||
num_channels = ( ( module_header[ 1080 ] - 48 ) * 10 ) + ( module_header[ 1081 ] - 48 );
|
||||
break;
|
||||
default:
|
||||
/* Not recognised. */
|
||||
num_channels = 0;
|
||||
break;
|
||||
}
|
||||
return num_channels;
|
||||
}
|
||||
|
||||
private static Pattern read_mod_pattern( DataInput data_input, int num_channels ) throws IOException {
|
||||
int input_idx, output_idx;
|
||||
int period, instrument, effect, effect_param;
|
||||
Pattern pattern;
|
||||
byte[] input_pattern_data, output_pattern_data;
|
||||
pattern = new Pattern();
|
||||
pattern.num_rows = 64;
|
||||
input_pattern_data = new byte[ 64 * num_channels * 4 ];
|
||||
output_pattern_data = new byte[ 64 * num_channels * 5 ];
|
||||
data_input.readFully( input_pattern_data );
|
||||
input_idx = 0;
|
||||
output_idx = 0;
|
||||
while( input_idx < input_pattern_data.length ) {
|
||||
period = ( input_pattern_data[ input_idx ] & 0x0F ) << 8;
|
||||
period = period | ( input_pattern_data[ input_idx + 1 ] & 0xFF );
|
||||
output_pattern_data[ output_idx ] = to_key( period );
|
||||
instrument = input_pattern_data[ input_idx ] & 0x10;
|
||||
instrument = instrument | ( ( input_pattern_data[ input_idx + 2 ] & 0xF0 ) >> 4 );
|
||||
output_pattern_data[ output_idx + 1 ] = ( byte ) instrument;
|
||||
effect = input_pattern_data[ input_idx + 2 ] & 0x0F;
|
||||
effect_param = input_pattern_data[ input_idx + 3 ] & 0xFF;
|
||||
if( effect == 0x01 && effect_param == 0 ) {
|
||||
/* Portamento up of zero has no effect. */
|
||||
effect = 0;
|
||||
}
|
||||
if( effect == 0x02 && effect_param == 0 ) {
|
||||
/* Portamento down of zero has no effect. */
|
||||
effect = 0;
|
||||
}
|
||||
if( effect == 0x08 && num_channels == 4 ) {
|
||||
/* Some Amiga mods use effect 0x08 for reasons other than panning.*/
|
||||
effect = 0;
|
||||
effect_param = 0;
|
||||
}
|
||||
if( effect == 0x0A && effect_param == 0 ) {
|
||||
/* Volume slide of zero has no effect.*/
|
||||
effect = 0;
|
||||
}
|
||||
if( effect == 0x05 && effect_param == 0 ) {
|
||||
/* Porta + Volume slide of zero has no effect.*/
|
||||
effect = 0x03;
|
||||
}
|
||||
if( effect == 0x06 && effect_param == 0 ) {
|
||||
/* Vibrato + Volume slide of zero has no effect.*/
|
||||
effect = 0x04;
|
||||
}
|
||||
output_pattern_data[ output_idx + 3 ] = ( byte ) effect;
|
||||
output_pattern_data[ output_idx + 4 ] = ( byte ) effect_param;
|
||||
input_idx += 4;
|
||||
output_idx += 5;
|
||||
}
|
||||
pattern.set_pattern_data( output_pattern_data );
|
||||
return pattern;
|
||||
}
|
||||
|
||||
private static Instrument read_mod_instrument( byte[] mod_header, int idx, DataInput data_input ) throws IOException {
|
||||
int header_offset, sample_data_length;
|
||||
int loop_start, loop_length, sample_idx, fine_tune;
|
||||
Instrument instrument;
|
||||
Sample sample;
|
||||
byte[] raw_sample_data;
|
||||
short[] sample_data;
|
||||
header_offset = ( idx - 1 ) * 30 + 20;
|
||||
instrument = new Instrument();
|
||||
instrument.name = ascii_text( mod_header, header_offset, 22 );
|
||||
sample = new Sample();
|
||||
sample_data_length = unsigned_short_be( mod_header, header_offset + 22 ) << 1;
|
||||
fine_tune = mod_header[ header_offset + 24 ] & 0x0F;
|
||||
if( fine_tune > 7 ) {
|
||||
fine_tune -= 16;
|
||||
}
|
||||
sample.transpose = ( fine_tune << IBXM.FP_SHIFT ) / 96;
|
||||
sample.volume = mod_header[ header_offset + 25 ] & 0x7F;
|
||||
loop_start = unsigned_short_be( mod_header, header_offset + 26 ) << 1;
|
||||
loop_length = unsigned_short_be( mod_header, header_offset + 28 ) << 1;
|
||||
if( loop_length < 4 ) {
|
||||
loop_length = 0;
|
||||
}
|
||||
raw_sample_data = new byte[ sample_data_length ];
|
||||
sample_data = new short[ sample_data_length ];
|
||||
try {
|
||||
data_input.readFully( raw_sample_data );
|
||||
} catch( EOFException e ) {
|
||||
System.out.println( "ProTracker: Instrument " + idx + " has samples missing." );
|
||||
}
|
||||
for( sample_idx = 0; sample_idx < raw_sample_data.length; sample_idx++ ) {
|
||||
sample_data[ sample_idx ] = ( short ) ( raw_sample_data[ sample_idx ] << 8 );
|
||||
}
|
||||
sample.set_sample_data( sample_data, loop_start, loop_length, false );
|
||||
instrument.set_num_samples( 1 );
|
||||
instrument.set_sample( 0, sample );
|
||||
return instrument;
|
||||
}
|
||||
|
||||
private static byte to_key( int period ) {
|
||||
int oct, key;
|
||||
if( period < 32 ) {
|
||||
key = 0;
|
||||
} else {
|
||||
oct = LogTable.log_2( 7256 ) - LogTable.log_2( period );
|
||||
if( oct < 0 ) {
|
||||
key = 0;
|
||||
} else {
|
||||
key = oct * 12;
|
||||
key = key >> ( IBXM.FP_SHIFT - 1 );
|
||||
key = ( key >> 1 ) + ( key & 1 );
|
||||
}
|
||||
}
|
||||
return ( byte ) key;
|
||||
}
|
||||
|
||||
private static int unsigned_short_be( byte[] buf, int offset ) {
|
||||
int value;
|
||||
value = ( buf[ offset ] & 0xFF ) << 8;
|
||||
value = value | ( buf[ offset + 1 ] & 0xFF );
|
||||
return value;
|
||||
}
|
||||
|
||||
private static String ascii_text( byte[] buffer, int offset, int length ) {
|
||||
int idx, chr;
|
||||
byte[] string_buffer;
|
||||
String string;
|
||||
string_buffer = new byte[ length ];
|
||||
for( idx = 0; idx < length; idx++ ) {
|
||||
chr = buffer[ offset + idx ];
|
||||
if( chr < 32 ) {
|
||||
chr = 32;
|
||||
}
|
||||
string_buffer[ idx ] = ( byte ) chr;
|
||||
}
|
||||
try {
|
||||
string = new String( string_buffer, 0, length, "ISO-8859-1" );
|
||||
} catch( UnsupportedEncodingException e ) {
|
||||
string = "";
|
||||
}
|
||||
return string;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,239 +0,0 @@
|
||||
|
||||
package ibxm;
|
||||
|
||||
public class Sample {
|
||||
public String name;
|
||||
public boolean set_panning;
|
||||
public int volume, panning;
|
||||
public int transpose;
|
||||
|
||||
private int loop_start, loop_length;
|
||||
private short[] sample_data;
|
||||
|
||||
/* For the sinc interpolator.*/
|
||||
private static final int POINT_SHIFT = 4;
|
||||
private static final int POINTS = 1 << POINT_SHIFT;
|
||||
private static final int OVERLAP = POINTS >> 1;
|
||||
private static final int INTERP_SHIFT = IBXM.FP_SHIFT - 4;
|
||||
private static final int INTERP_BITMASK = ( 1 << INTERP_SHIFT ) - 1;
|
||||
private static final short[] sinc_table = {
|
||||
0, -7, 27, -71, 142, -227, 299, 32439, 299, -227, 142, -71, 27, -7, 0, 0,
|
||||
0, 0, -5, 36, -142, 450, -1439, 32224, 2302, -974, 455, -190, 64, -15, 2, 0,
|
||||
0, 6, -33, 128, -391, 1042, -2894, 31584, 4540, -1765, 786, -318, 105, -25, 3, 0,
|
||||
0, 10, -55, 204, -597, 1533, -4056, 30535, 6977, -2573, 1121, -449, 148, -36, 5, 0,
|
||||
-1, 13, -71, 261, -757, 1916, -4922, 29105, 9568, -3366, 1448, -578, 191, -47, 7, 0,
|
||||
-1, 15, -81, 300, -870, 2185, -5498, 27328, 12263, -4109, 1749, -698, 232, -58, 9, 0,
|
||||
-1, 15, -86, 322, -936, 2343, -5800, 25249, 15006, -4765, 2011, -802, 269, -68, 10, 0,
|
||||
-1, 15, -87, 328, -957, 2394, -5849, 22920, 17738, -5298, 2215, -885, 299, -77, 12, 0,
|
||||
0, 14, -83, 319, -938, 2347, -5671, 20396, 20396, -5671, 2347, -938, 319, -83, 14, 0,
|
||||
0, 12, -77, 299, -885, 2215, -5298, 17738, 22920, -5849, 2394, -957, 328, -87, 15, -1,
|
||||
0, 10, -68, 269, -802, 2011, -4765, 15006, 25249, -5800, 2343, -936, 322, -86, 15, -1,
|
||||
0, 9, -58, 232, -698, 1749, -4109, 12263, 27328, -5498, 2185, -870, 300, -81, 15, -1,
|
||||
0, 7, -47, 191, -578, 1448, -3366, 9568, 29105, -4922, 1916, -757, 261, -71, 13, -1,
|
||||
0, 5, -36, 148, -449, 1121, -2573, 6977, 30535, -4056, 1533, -597, 204, -55, 10, 0,
|
||||
0, 3, -25, 105, -318, 786, -1765, 4540, 31584, -2894, 1042, -391, 128, -33, 6, 0,
|
||||
0, 2, -15, 64, -190, 455, -974, 2302, 32224, -1439, 450, -142, 36, -5, 0, 0,
|
||||
0, 0, -7, 27, -71, 142, -227, 299, 32439, 299, -227, 142, -71, 27, -7, 0
|
||||
};
|
||||
|
||||
public Sample() {
|
||||
name = "";
|
||||
set_sample_data( new short[ 0 ], 0, 0, false );
|
||||
}
|
||||
|
||||
public void set_sample_data( short[] data, int loop_start, int loop_length, boolean ping_pong ) {
|
||||
int offset;
|
||||
short sample;
|
||||
if( loop_start < 0 ) {
|
||||
loop_start = 0;
|
||||
}
|
||||
if( loop_start >= data.length ) {
|
||||
loop_start = data.length - 1;
|
||||
}
|
||||
if( loop_start + loop_length > data.length ) {
|
||||
loop_length = data.length - loop_start;
|
||||
}
|
||||
if( loop_length <= 1 ) {
|
||||
sample_data = new short[ OVERLAP + data.length + OVERLAP * 3 ];
|
||||
System.arraycopy( data, 0, sample_data, OVERLAP, data.length );
|
||||
offset = 0;
|
||||
while( offset < OVERLAP ) {
|
||||
sample = sample_data[ OVERLAP + data.length - 1 ];
|
||||
sample = ( short ) ( sample * ( OVERLAP - offset ) / OVERLAP );
|
||||
sample_data[ OVERLAP + data.length + offset ] = sample;
|
||||
offset += 1;
|
||||
}
|
||||
loop_start = OVERLAP + data.length + OVERLAP;
|
||||
loop_length = 1;
|
||||
} else {
|
||||
if( ping_pong ) {
|
||||
sample_data = new short[ OVERLAP + loop_start + loop_length * 2 + OVERLAP * 2 ];
|
||||
System.arraycopy( data, 0, sample_data, OVERLAP, loop_start + loop_length );
|
||||
offset = 0;
|
||||
while( offset < loop_length ) {
|
||||
sample = data[ loop_start + loop_length - offset - 1 ];
|
||||
sample_data[ OVERLAP + loop_start + loop_length + offset ] = sample;
|
||||
offset += 1;
|
||||
}
|
||||
loop_start = loop_start + OVERLAP;
|
||||
loop_length = loop_length * 2;
|
||||
} else {
|
||||
sample_data = new short[ OVERLAP + loop_start + loop_length + OVERLAP * 2 ];
|
||||
System.arraycopy( data, 0, sample_data, OVERLAP, loop_start + loop_length );
|
||||
loop_start = loop_start + OVERLAP;
|
||||
}
|
||||
offset = 0;
|
||||
while( offset < OVERLAP * 2 ) {
|
||||
sample = sample_data[ loop_start + offset ];
|
||||
sample_data[ loop_start + loop_length + offset ] = sample;
|
||||
offset += 1;
|
||||
}
|
||||
}
|
||||
this.loop_start = loop_start;
|
||||
this.loop_length = loop_length;
|
||||
}
|
||||
|
||||
public void resample_nearest(
|
||||
int sample_idx, int sample_frac, int step, int left_gain, int right_gain,
|
||||
int[] mix_buffer, int frame_offset, int frames ) {
|
||||
int loop_end, offset, end, max_sample_idx;
|
||||
sample_idx += OVERLAP;
|
||||
loop_end = loop_start + loop_length - 1;
|
||||
offset = frame_offset << 1;
|
||||
end = ( frame_offset + frames - 1 ) << 1;
|
||||
while( frames > 0 ) {
|
||||
if( sample_idx > loop_end ) {
|
||||
if( loop_length <= 1 ) {
|
||||
break;
|
||||
}
|
||||
sample_idx = loop_start + ( sample_idx - loop_start ) % loop_length;
|
||||
}
|
||||
max_sample_idx = sample_idx + ( ( sample_frac + ( frames - 1 ) * step ) >> IBXM.FP_SHIFT );
|
||||
if( max_sample_idx > loop_end ) {
|
||||
while( sample_idx <= loop_end ) {
|
||||
mix_buffer[ offset++ ] += sample_data[ sample_idx ] * left_gain >> IBXM.FP_SHIFT;
|
||||
mix_buffer[ offset++ ] += sample_data[ sample_idx ] * right_gain >> IBXM.FP_SHIFT;
|
||||
sample_frac += step;
|
||||
sample_idx += sample_frac >> IBXM.FP_SHIFT;
|
||||
sample_frac &= IBXM.FP_MASK;
|
||||
}
|
||||
} else {
|
||||
while( offset <= end ) {
|
||||
mix_buffer[ offset++ ] += sample_data[ sample_idx ] * left_gain >> IBXM.FP_SHIFT;
|
||||
mix_buffer[ offset++ ] += sample_data[ sample_idx ] * right_gain >> IBXM.FP_SHIFT;
|
||||
sample_frac += step;
|
||||
sample_idx += sample_frac >> IBXM.FP_SHIFT;
|
||||
sample_frac &= IBXM.FP_MASK;
|
||||
}
|
||||
}
|
||||
frames = ( end - offset + 2 ) >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
public void resample_linear(
|
||||
int sample_idx, int sample_frac, int step, int left_gain, int right_gain,
|
||||
int[] mix_buffer, int frame_offset, int frames ) {
|
||||
int loop_end, offset, end, max_sample_idx, amplitude;
|
||||
sample_idx += OVERLAP;
|
||||
loop_end = loop_start + loop_length - 1;
|
||||
offset = frame_offset << 1;
|
||||
end = ( frame_offset + frames - 1 ) << 1;
|
||||
while( frames > 0 ) {
|
||||
if( sample_idx > loop_end ) {
|
||||
if( loop_length <= 1 ) {
|
||||
break;
|
||||
}
|
||||
sample_idx = loop_start + ( sample_idx - loop_start ) % loop_length;
|
||||
}
|
||||
max_sample_idx = sample_idx + ( ( sample_frac + ( frames - 1 ) * step ) >> IBXM.FP_SHIFT );
|
||||
if( max_sample_idx > loop_end ) {
|
||||
while( sample_idx <= loop_end ) {
|
||||
amplitude = sample_data[ sample_idx ];
|
||||
amplitude += ( sample_data[ sample_idx + 1 ] - amplitude ) * sample_frac >> IBXM.FP_SHIFT;
|
||||
mix_buffer[ offset++ ] += amplitude * left_gain >> IBXM.FP_SHIFT;
|
||||
mix_buffer[ offset++ ] += amplitude * right_gain >> IBXM.FP_SHIFT;
|
||||
sample_frac += step;
|
||||
sample_idx += sample_frac >> IBXM.FP_SHIFT;
|
||||
sample_frac &= IBXM.FP_MASK;
|
||||
}
|
||||
} else {
|
||||
while( offset <= end ) {
|
||||
amplitude = sample_data[ sample_idx ];
|
||||
amplitude += ( sample_data[ sample_idx + 1 ] - amplitude ) * sample_frac >> IBXM.FP_SHIFT;
|
||||
mix_buffer[ offset++ ] += amplitude * left_gain >> IBXM.FP_SHIFT;
|
||||
mix_buffer[ offset++ ] += amplitude * right_gain >> IBXM.FP_SHIFT;
|
||||
sample_frac += step;
|
||||
sample_idx += sample_frac >> IBXM.FP_SHIFT;
|
||||
sample_frac &= IBXM.FP_MASK;
|
||||
}
|
||||
}
|
||||
frames = ( end - offset + 2 ) >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
public void resample_sinc(
|
||||
int sample_idx, int sample_frac, int step, int left_gain, int right_gain,
|
||||
int[] mix_buffer, int frame_offset, int frames ) {
|
||||
int offset, end, loop_end, table_idx, a1, a2, amplitude;
|
||||
loop_end = loop_start + loop_length - 1;
|
||||
offset = frame_offset << 1;
|
||||
end = ( frame_offset + frames - 1 ) << 1;
|
||||
while( offset <= end ) {
|
||||
if( sample_idx > loop_end ) {
|
||||
if( loop_length <= 1 ) {
|
||||
break;
|
||||
}
|
||||
sample_idx = loop_start + ( sample_idx - loop_start ) % loop_length;
|
||||
}
|
||||
table_idx = ( sample_frac >> INTERP_SHIFT ) << POINT_SHIFT;
|
||||
a1 = sinc_table[ table_idx + 0 ] * sample_data[ sample_idx + 0 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 1 ] * sample_data[ sample_idx + 1 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 2 ] * sample_data[ sample_idx + 2 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 3 ] * sample_data[ sample_idx + 3 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 4 ] * sample_data[ sample_idx + 4 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 5 ] * sample_data[ sample_idx + 5 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 6 ] * sample_data[ sample_idx + 6 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 7 ] * sample_data[ sample_idx + 7 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 8 ] * sample_data[ sample_idx + 8 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 9 ] * sample_data[ sample_idx + 9 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 10 ] * sample_data[ sample_idx + 10 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 11 ] * sample_data[ sample_idx + 11 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 12 ] * sample_data[ sample_idx + 12 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 13 ] * sample_data[ sample_idx + 13 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 14 ] * sample_data[ sample_idx + 14 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 15 ] * sample_data[ sample_idx + 15 ] >> 15;
|
||||
a2 = sinc_table[ table_idx + 16 ] * sample_data[ sample_idx + 0 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 17 ] * sample_data[ sample_idx + 1 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 18 ] * sample_data[ sample_idx + 2 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 19 ] * sample_data[ sample_idx + 3 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 20 ] * sample_data[ sample_idx + 4 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 21 ] * sample_data[ sample_idx + 5 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 22 ] * sample_data[ sample_idx + 6 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 23 ] * sample_data[ sample_idx + 7 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 24 ] * sample_data[ sample_idx + 8 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 25 ] * sample_data[ sample_idx + 9 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 26 ] * sample_data[ sample_idx + 10 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 27 ] * sample_data[ sample_idx + 11 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 28 ] * sample_data[ sample_idx + 12 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 29 ] * sample_data[ sample_idx + 13 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 30 ] * sample_data[ sample_idx + 14 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 31 ] * sample_data[ sample_idx + 15 ] >> 15;
|
||||
amplitude = a1 + ( ( a2 - a1 ) * ( sample_frac & INTERP_BITMASK ) >> INTERP_SHIFT );
|
||||
mix_buffer[ offset ] += amplitude * left_gain >> IBXM.FP_SHIFT;
|
||||
mix_buffer[ offset + 1 ] += amplitude * right_gain >> IBXM.FP_SHIFT;
|
||||
offset += 2;
|
||||
sample_frac += step;
|
||||
sample_idx += sample_frac >> IBXM.FP_SHIFT;
|
||||
sample_frac &= IBXM.FP_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean has_finished( int sample_idx ) {
|
||||
boolean finished;
|
||||
finished = false;
|
||||
if( loop_length <= 1 && sample_idx > loop_start ) {
|
||||
finished = true;
|
||||
}
|
||||
return finished;
|
||||
}
|
||||
}
|
||||
@@ -1,489 +0,0 @@
|
||||
|
||||
package ibxm;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class ScreamTracker3 {
|
||||
private static final int[] effect_map = new int[] {
|
||||
0xFF,
|
||||
0x25, /* A: Set Speed.*/
|
||||
0x0B, /* B: Pattern Jump.*/
|
||||
0x0D, /* C: Pattern Break.*/
|
||||
0x0A, /* D: Volume Slide.*/
|
||||
0x02, /* E: Portamento Down.*/
|
||||
0x01, /* F: Portamento Up.*/
|
||||
0x03, /* G: Tone Portamento.*/
|
||||
0x04, /* H: Vibrato.*/
|
||||
0x1D, /* I: Tremor.*/
|
||||
0x00, /* J: Arpeggio.*/
|
||||
0x06, /* K: Vibrato + Volume Slide.*/
|
||||
0x05, /* L: Tone Portamento + Volume Slide.*/
|
||||
0xFF, /* M: */
|
||||
0xFF, /* N: */
|
||||
0x09, /* O: Sample Offset.*/
|
||||
0xFF, /* P: */
|
||||
0x1B, /* Q: Retrig + Volume Slide.*/
|
||||
0x07, /* R: Tremolo.*/
|
||||
0x0E, /* S: Extended Effects.*/
|
||||
0x0F, /* T: Set Tempo.*/
|
||||
0x24, /* U: Fine Vibrato.*/
|
||||
0x10, /* V: Set Global Volume. */
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF
|
||||
};
|
||||
|
||||
private static final int[] effect_s_map = new int[] {
|
||||
0x00, /* 0: Set Filter.*/
|
||||
0x03, /* 1: Glissando.*/
|
||||
0x05, /* 2: Set Fine Tune.*/
|
||||
0x04, /* 3: Set Vibrato Waveform.*/
|
||||
0x07, /* 4: Set Tremolo Waveform.*/
|
||||
0xFF, /* 5: */
|
||||
0xFF, /* 6: */
|
||||
0xFF, /* 7: */
|
||||
0x08, /* 8: Set Panning.*/
|
||||
0xFF, /* 9: */
|
||||
0x09, /* A: Stereo Control.*/
|
||||
0x06, /* B: Pattern Loop.*/
|
||||
0x0C, /* C: Note Cut.*/
|
||||
0x0D, /* D: Note Delay.*/
|
||||
0x0E, /* E: Pattern Delay.*/
|
||||
0x0F /* F: Invert Loop.*/
|
||||
};
|
||||
|
||||
public static boolean is_s3m( byte[] header_96_bytes ) {
|
||||
String s3m_identifier;
|
||||
s3m_identifier = ascii_text( header_96_bytes, 44, 4 );
|
||||
return s3m_identifier.equals( "SCRM" );
|
||||
}
|
||||
|
||||
public static Module load_s3m( byte[] header_96_bytes, DataInput data_input ) throws IOException {
|
||||
int num_pattern_orders, num_instruments, num_patterns, num_channels;
|
||||
int flags, tracker_version, master_volume, panning, channel_config, sequence_length;
|
||||
int instrument_idx, pattern_idx, channel_idx, order_idx, panning_offset;
|
||||
boolean signed_samples, stereo_mode, default_panning;
|
||||
int[] channel_map, sequence;
|
||||
byte[] s3m_file;
|
||||
Module module;
|
||||
Instrument instrument;
|
||||
s3m_file = read_s3m_file( header_96_bytes, data_input );
|
||||
module = new Module();
|
||||
module.song_title = ascii_text( s3m_file, 0, 28 );
|
||||
num_pattern_orders = get_num_pattern_orders( s3m_file );
|
||||
num_instruments = get_num_instruments( s3m_file );
|
||||
num_patterns = get_num_patterns( s3m_file );
|
||||
flags = unsigned_short_le( s3m_file, 38 );
|
||||
tracker_version = unsigned_short_le( s3m_file, 40 );
|
||||
if( ( flags & 0x40 ) == 0x40 || tracker_version == 0x1300 ) {
|
||||
module.fast_volume_slides = true;
|
||||
}
|
||||
signed_samples = false;
|
||||
if( unsigned_short_le( s3m_file, 42 ) == 0x01 ) {
|
||||
signed_samples = true;
|
||||
}
|
||||
module.global_volume = s3m_file[ 48 ] & 0xFF;
|
||||
module.default_speed = s3m_file[ 49 ] & 0xFF;
|
||||
module.default_tempo = s3m_file[ 50 ] & 0xFF;
|
||||
master_volume = s3m_file[ 51 ] & 0x7F;
|
||||
module.channel_gain = ( master_volume << IBXM.FP_SHIFT ) >> 7;
|
||||
stereo_mode = ( s3m_file[ 51 ] & 0x80 ) == 0x80;
|
||||
default_panning = ( s3m_file[ 53 ] & 0xFF ) == 0xFC;
|
||||
channel_map = new int[ 32 ];
|
||||
num_channels = 0;
|
||||
for( channel_idx = 0; channel_idx < 32; channel_idx++ ) {
|
||||
channel_config = s3m_file[ 64 + channel_idx ] & 0xFF;
|
||||
channel_map[ channel_idx ] = -1;
|
||||
if( channel_config < 16 ) {
|
||||
channel_map[ channel_idx ] = num_channels;
|
||||
num_channels += 1;
|
||||
}
|
||||
}
|
||||
module.set_num_channels( num_channels );
|
||||
panning_offset = 96 + num_pattern_orders + num_instruments * 2 + num_patterns * 2;
|
||||
for( channel_idx = 0; channel_idx < 32; channel_idx++ ) {
|
||||
if( channel_map[ channel_idx ] < 0 ) continue;
|
||||
panning = 7;
|
||||
if( stereo_mode ) {
|
||||
panning = 12;
|
||||
if( ( s3m_file[ 64 + channel_idx ] & 0xFF ) < 8 ) {
|
||||
panning = 3;
|
||||
}
|
||||
}
|
||||
if( default_panning ) {
|
||||
flags = s3m_file[ panning_offset + channel_idx ] & 0xFF;
|
||||
if( ( flags & 0x20 ) == 0x20 ) {
|
||||
panning = flags & 0xF;
|
||||
}
|
||||
}
|
||||
module.set_initial_panning( channel_map[ channel_idx ], panning * 17 );
|
||||
}
|
||||
sequence = read_s3m_sequence( s3m_file );
|
||||
module.set_sequence_length( sequence.length );
|
||||
for( order_idx = 0; order_idx < sequence.length; order_idx++ ) {
|
||||
module.set_sequence( order_idx, sequence[ order_idx ] );
|
||||
}
|
||||
module.set_num_instruments( num_instruments );
|
||||
for( instrument_idx = 0; instrument_idx < num_instruments; instrument_idx++ ) {
|
||||
instrument = read_s3m_instrument( s3m_file, instrument_idx, signed_samples );
|
||||
module.set_instrument( instrument_idx + 1, instrument );
|
||||
}
|
||||
module.set_num_patterns( num_patterns );
|
||||
for( pattern_idx = 0; pattern_idx < num_patterns; pattern_idx++ ) {
|
||||
module.set_pattern( pattern_idx, read_s3m_pattern( s3m_file, pattern_idx, channel_map ) );
|
||||
}
|
||||
return module;
|
||||
}
|
||||
|
||||
private static int[] read_s3m_sequence( byte[] s3m_file ) {
|
||||
int num_pattern_orders, sequence_length;
|
||||
int sequence_idx, order_idx, pattern_order;
|
||||
int[] sequence;
|
||||
num_pattern_orders = get_num_pattern_orders( s3m_file );
|
||||
sequence_length = 0;
|
||||
for( order_idx = 0; order_idx < num_pattern_orders; order_idx++ ) {
|
||||
pattern_order = s3m_file[ 96 + order_idx ] & 0xFF;
|
||||
if( pattern_order == 255 ) {
|
||||
break;
|
||||
} else if( pattern_order < 254 ) {
|
||||
sequence_length += 1;
|
||||
}
|
||||
}
|
||||
sequence = new int[ sequence_length ];
|
||||
sequence_idx = 0;
|
||||
for( order_idx = 0; order_idx < num_pattern_orders; order_idx++ ) {
|
||||
pattern_order = s3m_file[ 96 + order_idx ] & 0xFF;
|
||||
if( pattern_order == 255 ) {
|
||||
break;
|
||||
} else if( pattern_order < 254 ) {
|
||||
sequence[ sequence_idx ] = pattern_order;
|
||||
sequence_idx += 1;
|
||||
}
|
||||
}
|
||||
return sequence;
|
||||
}
|
||||
|
||||
private static Instrument read_s3m_instrument( byte[] s3m_file, int instrument_idx, boolean signed_samples ) {
|
||||
int instrument_offset;
|
||||
int sample_data_offset, sample_data_length;
|
||||
int loop_start, loop_length, c2_rate, sample_idx, amplitude;
|
||||
boolean sixteen_bit;
|
||||
Instrument instrument;
|
||||
Sample sample;
|
||||
short[] sample_data;
|
||||
instrument_offset = get_instrument_offset( s3m_file, instrument_idx );
|
||||
instrument = new Instrument();
|
||||
instrument.name = ascii_text( s3m_file, instrument_offset + 48, 28 );
|
||||
sample = new Sample();
|
||||
if( s3m_file[ instrument_offset ] == 1 ) {
|
||||
sample_data_length = get_sample_data_length( s3m_file, instrument_offset );
|
||||
loop_start = unsigned_short_le( s3m_file, instrument_offset + 20 );
|
||||
loop_length = unsigned_short_le( s3m_file, instrument_offset + 24 ) - loop_start;
|
||||
sample.volume = s3m_file[ instrument_offset + 28 ] & 0xFF;
|
||||
if( s3m_file[ instrument_offset + 30 ] != 0 ) {
|
||||
throw new IllegalArgumentException( "ScreamTracker3: Packed samples not supported!" );
|
||||
}
|
||||
if( ( s3m_file[ instrument_offset + 31 ] & 0x01 ) == 0 ) {
|
||||
loop_length = 0;
|
||||
}
|
||||
if( ( s3m_file[ instrument_offset + 31 ] & 0x02 ) != 0 ) {
|
||||
throw new IllegalArgumentException( "ScreamTracker3: Stereo samples not supported!" );
|
||||
}
|
||||
sixteen_bit = ( s3m_file[ instrument_offset + 31 ] & 0x04 ) != 0;
|
||||
c2_rate = unsigned_short_le( s3m_file, instrument_offset + 32 );
|
||||
sample.transpose = LogTable.log_2( c2_rate ) - LogTable.log_2( 8363 );
|
||||
sample_data_offset = get_sample_data_offset( s3m_file, instrument_offset );
|
||||
if( sixteen_bit ) {
|
||||
if( signed_samples ) {
|
||||
throw new IllegalArgumentException( "ScreamTracker3: Signed 16-bit samples not supported!" );
|
||||
}
|
||||
sample_data_length >>= 1;
|
||||
sample_data = new short[ sample_data_length ];
|
||||
for( sample_idx = 0; sample_idx < sample_data_length; sample_idx++ ) {
|
||||
amplitude = s3m_file[ sample_data_offset + sample_idx * 2 ] & 0xFF;
|
||||
amplitude |= ( s3m_file[ sample_data_offset + sample_idx * 2 + 1 ] & 0xFF ) << 8;
|
||||
sample_data[ sample_idx ] = ( short ) ( amplitude - 32768 );
|
||||
}
|
||||
} else {
|
||||
sample_data = new short[ sample_data_length ];
|
||||
if( signed_samples ) {
|
||||
for( sample_idx = 0; sample_idx < sample_data_length; sample_idx++ ) {
|
||||
amplitude = s3m_file[ sample_data_offset + sample_idx ] << 8;
|
||||
sample_data[ sample_idx ] = ( short ) amplitude;
|
||||
}
|
||||
} else {
|
||||
for( sample_idx = 0; sample_idx < sample_data_length; sample_idx++ ) {
|
||||
amplitude = ( s3m_file[ sample_data_offset + sample_idx ] & 0xFF ) << 8;
|
||||
sample_data[ sample_idx ] = ( short ) ( amplitude - 32768 );
|
||||
}
|
||||
}
|
||||
}
|
||||
sample.set_sample_data( sample_data, loop_start, loop_length, false );
|
||||
}
|
||||
instrument.set_num_samples( 1 );
|
||||
instrument.set_sample( 0, sample );
|
||||
return instrument;
|
||||
}
|
||||
|
||||
private static Pattern read_s3m_pattern( byte[] s3m_file, int pattern_idx, int[] channel_map ) {
|
||||
int pattern_offset;
|
||||
int num_channels, num_notes;
|
||||
int row_idx, channel_idx, note_idx;
|
||||
int token, key, volume_column, effect, effect_param;
|
||||
byte[] pattern_data;
|
||||
Pattern pattern;
|
||||
num_channels = 0;
|
||||
for( channel_idx = 0; channel_idx < 32; channel_idx++ ) {
|
||||
if( channel_map[ channel_idx ] >= num_channels ) {
|
||||
num_channels = channel_idx + 1;
|
||||
}
|
||||
}
|
||||
num_notes = num_channels * 64;
|
||||
pattern_data = new byte[ num_notes * 5 ];
|
||||
row_idx = 0;
|
||||
pattern_offset = get_pattern_offset( s3m_file, pattern_idx ) + 2;
|
||||
while( row_idx < 64 ) {
|
||||
token = s3m_file[ pattern_offset ] & 0xFF;
|
||||
pattern_offset += 1;
|
||||
if( token > 0 ) {
|
||||
channel_idx = channel_map[ token & 0x1F ];
|
||||
note_idx = ( num_channels * row_idx + channel_idx ) * 5;
|
||||
if( ( token & 0x20 ) == 0x20 ) {
|
||||
/* Key + Instrument.*/
|
||||
if( channel_idx >= 0 ) {
|
||||
key = s3m_file[ pattern_offset ] & 0xFF;
|
||||
if( key == 255 ) {
|
||||
key = 0;
|
||||
} else if( key == 254 ) {
|
||||
key = 97;
|
||||
} else {
|
||||
key = ( ( key & 0xF0 ) >> 4 ) * 12 + ( key & 0x0F ) + 1;
|
||||
while( key > 96 ) {
|
||||
key = key - 12;
|
||||
}
|
||||
}
|
||||
pattern_data[ note_idx ] = ( byte ) key;
|
||||
pattern_data[ note_idx + 1 ] = s3m_file[ pattern_offset + 1 ];
|
||||
}
|
||||
pattern_offset += 2;
|
||||
}
|
||||
if( ( token & 0x40 ) == 0x40 ) {
|
||||
/* Volume.*/
|
||||
if( channel_idx >= 0 ) {
|
||||
volume_column = ( s3m_file[ pattern_offset ] & 0xFF ) + 0x10;
|
||||
pattern_data[ note_idx + 2 ] = ( byte ) volume_column;
|
||||
}
|
||||
pattern_offset += 1;
|
||||
}
|
||||
if( ( token & 0x80 ) == 0x80 ) {
|
||||
/* Effect + Param.*/
|
||||
if( channel_idx >= 0 ) {
|
||||
effect = s3m_file[ pattern_offset ] & 0xFF;
|
||||
effect_param = s3m_file[ pattern_offset + 1 ] & 0xFF;
|
||||
effect = effect_map[ effect & 0x1F ];
|
||||
if( effect == 0xFF ) {
|
||||
effect = 0;
|
||||
effect_param = 0;
|
||||
}
|
||||
if( effect == 0x0E ) {
|
||||
effect = effect_s_map[ ( effect_param & 0xF0 ) >> 4 ];
|
||||
effect_param = effect_param & 0x0F;
|
||||
switch( effect ) {
|
||||
case 0x08:
|
||||
effect = 0x08;
|
||||
effect_param = effect_param * 17;
|
||||
break;
|
||||
case 0x09:
|
||||
effect = 0x08;
|
||||
if( effect_param > 7 ) {
|
||||
effect_param -= 8;
|
||||
} else {
|
||||
effect_param += 8;
|
||||
}
|
||||
effect_param = effect_param * 17;
|
||||
break;
|
||||
case 0xFF:
|
||||
effect = 0;
|
||||
effect_param = 0;
|
||||
break;
|
||||
default:
|
||||
effect_param = ( ( effect & 0x0F ) << 4 ) | ( effect_param & 0x0F );
|
||||
effect = 0x0E;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pattern_data[ note_idx + 3 ] = ( byte ) effect;
|
||||
pattern_data[ note_idx + 4 ] = ( byte ) effect_param;
|
||||
}
|
||||
pattern_offset += 2;
|
||||
}
|
||||
} else {
|
||||
row_idx += 1;
|
||||
}
|
||||
}
|
||||
pattern = new Pattern();
|
||||
pattern.num_rows = 64;
|
||||
pattern.set_pattern_data( pattern_data );
|
||||
return pattern;
|
||||
}
|
||||
|
||||
private static byte[] read_s3m_file( byte[] header_96_bytes, DataInput data_input ) throws IOException {
|
||||
int s3m_file_length;
|
||||
int num_pattern_orders, num_instruments, num_patterns;
|
||||
int instrument_idx, pattern_idx;
|
||||
int instrument_offset, sample_data_offset, pattern_offset;
|
||||
byte[] s3m_file;
|
||||
if( !is_s3m( header_96_bytes ) ) {
|
||||
throw new IllegalArgumentException( "ScreamTracker3: Not an S3M file!" );
|
||||
}
|
||||
s3m_file = header_96_bytes;
|
||||
s3m_file_length = header_96_bytes.length;
|
||||
num_pattern_orders = get_num_pattern_orders( s3m_file );
|
||||
num_instruments = get_num_instruments( s3m_file );
|
||||
num_patterns = get_num_patterns( s3m_file );
|
||||
s3m_file_length += num_pattern_orders;
|
||||
s3m_file_length += num_instruments * 2;
|
||||
s3m_file_length += num_patterns * 2;
|
||||
/* Read enough of file to calculate the length.*/
|
||||
s3m_file = read_more( s3m_file, s3m_file_length, data_input );
|
||||
for( instrument_idx = 0; instrument_idx < num_instruments; instrument_idx++ ) {
|
||||
instrument_offset = get_instrument_offset( s3m_file, instrument_idx );
|
||||
instrument_offset += 80;
|
||||
if( instrument_offset > s3m_file_length ) {
|
||||
s3m_file_length = instrument_offset;
|
||||
}
|
||||
}
|
||||
for( pattern_idx = 0; pattern_idx < num_patterns; pattern_idx++ ) {
|
||||
pattern_offset = get_pattern_offset( s3m_file, pattern_idx );
|
||||
pattern_offset += 2;
|
||||
if( pattern_offset > s3m_file_length ) {
|
||||
s3m_file_length = pattern_offset;
|
||||
}
|
||||
}
|
||||
s3m_file = read_more( s3m_file, s3m_file_length, data_input );
|
||||
/* Read rest of file.*/
|
||||
for( instrument_idx = 0; instrument_idx < num_instruments; instrument_idx++ ) {
|
||||
instrument_offset = get_instrument_offset( s3m_file, instrument_idx );
|
||||
sample_data_offset = get_sample_data_offset( s3m_file, instrument_offset );
|
||||
sample_data_offset += get_sample_data_length( s3m_file, instrument_offset );
|
||||
if( sample_data_offset > s3m_file_length ) {
|
||||
s3m_file_length = sample_data_offset;
|
||||
}
|
||||
}
|
||||
for( pattern_idx = 0; pattern_idx < num_patterns; pattern_idx++ ) {
|
||||
pattern_offset = get_pattern_offset( s3m_file, pattern_idx );
|
||||
pattern_offset += get_pattern_length( s3m_file, pattern_offset );
|
||||
pattern_offset += 2;
|
||||
if( pattern_offset > s3m_file_length ) {
|
||||
s3m_file_length = pattern_offset;
|
||||
}
|
||||
}
|
||||
s3m_file = read_more( s3m_file, s3m_file_length, data_input );
|
||||
return s3m_file;
|
||||
}
|
||||
|
||||
private static int get_num_pattern_orders( byte[] s3m_file ) {
|
||||
int num_pattern_orders;
|
||||
num_pattern_orders = unsigned_short_le( s3m_file, 32 );
|
||||
return num_pattern_orders;
|
||||
}
|
||||
|
||||
private static int get_num_instruments( byte[] s3m_file ) {
|
||||
int num_instruments;
|
||||
num_instruments = unsigned_short_le( s3m_file, 34 );
|
||||
return num_instruments;
|
||||
}
|
||||
|
||||
private static int get_num_patterns( byte[] s3m_file ) {
|
||||
int num_patterns;
|
||||
num_patterns = unsigned_short_le( s3m_file, 36 );
|
||||
return num_patterns;
|
||||
}
|
||||
|
||||
private static int get_instrument_offset( byte[] s3m_file, int instrument_idx ) {
|
||||
int instrument_offset, pointer_offset;
|
||||
pointer_offset = 96 + get_num_pattern_orders( s3m_file );
|
||||
instrument_offset = unsigned_short_le( s3m_file, pointer_offset + instrument_idx * 2 ) << 4;
|
||||
return instrument_offset;
|
||||
}
|
||||
|
||||
private static int get_sample_data_offset( byte[] s3m_file, int instrument_offset ) {
|
||||
int sample_data_offset;
|
||||
sample_data_offset = 0;
|
||||
if( s3m_file[ instrument_offset ] == 1 ) {
|
||||
sample_data_offset = ( s3m_file[ instrument_offset + 13 ] & 0xFF ) << 20;
|
||||
sample_data_offset |= unsigned_short_le( s3m_file, instrument_offset + 14 ) << 4;
|
||||
}
|
||||
return sample_data_offset;
|
||||
}
|
||||
|
||||
private static int get_sample_data_length( byte[] s3m_file, int instrument_offset ) {
|
||||
int sample_data_length;
|
||||
boolean sixteen_bit;
|
||||
sample_data_length = 0;
|
||||
if( s3m_file[ instrument_offset ] == 1 ) {
|
||||
sample_data_length = unsigned_short_le( s3m_file, instrument_offset + 16 );
|
||||
sixteen_bit = ( s3m_file[ instrument_offset + 31 ] & 0x04 ) != 0;
|
||||
if( sixteen_bit ) {
|
||||
sample_data_length <<= 1;
|
||||
}
|
||||
}
|
||||
return sample_data_length;
|
||||
}
|
||||
|
||||
private static int get_pattern_offset( byte[] s3m_file, int pattern_idx ) {
|
||||
int pattern_offset, pointer_offset;
|
||||
pointer_offset = 96 + get_num_pattern_orders( s3m_file );
|
||||
pointer_offset += get_num_instruments( s3m_file ) * 2;
|
||||
pattern_offset = unsigned_short_le( s3m_file, pointer_offset + pattern_idx * 2 ) << 4;
|
||||
return pattern_offset;
|
||||
}
|
||||
|
||||
private static int get_pattern_length( byte[] s3m_file, int pattern_offset ) {
|
||||
int pattern_length;
|
||||
pattern_length = unsigned_short_le( s3m_file, pattern_offset );
|
||||
return pattern_length;
|
||||
}
|
||||
|
||||
private static byte[] read_more( byte[] old_data, int new_length, DataInput data_input ) throws IOException {
|
||||
byte[] new_data;
|
||||
new_data = old_data;
|
||||
if( new_length > old_data.length ) {
|
||||
new_data = new byte[ new_length ];
|
||||
System.arraycopy( old_data, 0, new_data, 0, old_data.length );
|
||||
try {
|
||||
data_input.readFully( new_data, old_data.length, new_data.length - old_data.length );
|
||||
} catch( EOFException e ) {
|
||||
System.out.println( "ScreamTracker3: Module has been truncated!" );
|
||||
}
|
||||
}
|
||||
return new_data;
|
||||
}
|
||||
|
||||
private static int unsigned_short_le( byte[] buffer, int offset ) {
|
||||
int value;
|
||||
value = buffer[ offset ] & 0xFF;
|
||||
value = value | ( ( buffer[ offset + 1 ] & 0xFF ) << 8 );
|
||||
return value;
|
||||
}
|
||||
|
||||
private static String ascii_text( byte[] buffer, int offset, int length ) {
|
||||
int idx, chr;
|
||||
byte[] string_buffer;
|
||||
String string;
|
||||
string_buffer = new byte[ length ];
|
||||
for( idx = 0; idx < length; idx++ ) {
|
||||
chr = buffer[ offset + idx ];
|
||||
if( chr < 32 ) {
|
||||
chr = 32;
|
||||
}
|
||||
string_buffer[ idx ] = ( byte ) chr;
|
||||
}
|
||||
try {
|
||||
string = new String( string_buffer, 0, length, "ISO-8859-1" );
|
||||
} catch( UnsupportedEncodingException e ) {
|
||||
string = "";
|
||||
}
|
||||
return string;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
package net.minecraftforge.client;
|
||||
|
||||
import net.minecraft.world.EnumGameType;
|
||||
import net.minecraft.util.EnumOS;
|
||||
import net.minecraft.client.settings.EnumOptions;
|
||||
import net.minecraft.item.EnumRarity;
|
||||
import net.minecraftforge.common.EnumHelper;
|
||||
|
||||
public class EnumHelperClient extends EnumHelper
|
||||
{
|
||||
|
||||
private static Class[][] clentTypes =
|
||||
{
|
||||
{EnumGameType.class, int.class, String.class},
|
||||
{EnumOptions.class, String.class, boolean.class, boolean.class},
|
||||
{EnumOS.class},
|
||||
{EnumRarity.class, int.class, String.class}
|
||||
};
|
||||
|
||||
public static EnumGameType addGameType(String name, int id, String displayName)
|
||||
{
|
||||
return addEnum(EnumGameType.class, name, id, displayName);
|
||||
}
|
||||
|
||||
public static EnumOptions addOptions(String name, String langName, boolean isSlider, boolean isToggle)
|
||||
{
|
||||
return addEnum(EnumOptions.class, name, langName, isSlider, isToggle);
|
||||
}
|
||||
|
||||
public static EnumOS addOS2(String name)
|
||||
{
|
||||
return addEnum(EnumOS.class, name);
|
||||
}
|
||||
|
||||
public static EnumRarity addRarity(String name, int color, String displayName)
|
||||
{
|
||||
return addEnum(EnumRarity.class, name, color, displayName);
|
||||
}
|
||||
|
||||
public static <T extends Enum<? >> T addEnum(Class<T> enumType, String enumName, Object... paramValues)
|
||||
{
|
||||
return addEnum(clentTypes, enumType, enumName, paramValues);
|
||||
}
|
||||
}
|
||||
@@ -1,286 +0,0 @@
|
||||
package net.minecraftforge.client;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Random;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import org.lwjgl.LWJGLException;
|
||||
import org.lwjgl.opengl.Display;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
import org.lwjgl.opengl.PixelFormat;
|
||||
|
||||
import cpw.mods.fml.client.FMLClientHandler;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.entity.item.EntityItem;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemBlock;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.MathHelper;
|
||||
import net.minecraft.util.MovingObjectPosition;
|
||||
import net.minecraft.client.model.ModelBiped;
|
||||
import net.minecraft.client.renderer.RenderBlocks;
|
||||
import net.minecraft.client.renderer.RenderGlobal;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.entity.RenderItem;
|
||||
import net.minecraft.client.renderer.texture.TextureManager;
|
||||
import net.minecraft.client.renderer.texture.TextureMap;
|
||||
import net.minecraftforge.client.IItemRenderer.ItemRenderType;
|
||||
import net.minecraftforge.client.event.DrawBlockHighlightEvent;
|
||||
import net.minecraftforge.client.event.RenderWorldLastEvent;
|
||||
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import static net.minecraftforge.client.IItemRenderer.ItemRenderType.*;
|
||||
import static net.minecraftforge.client.IItemRenderer.ItemRendererHelper.*;
|
||||
|
||||
public class ForgeHooksClient
|
||||
{
|
||||
static TextureManager engine()
|
||||
{
|
||||
return FMLClientHandler.instance().getClient().renderEngine;
|
||||
}
|
||||
|
||||
public static String getArmorTexture(Entity entity, ItemStack armor, String _default, int slot, int layer, String type)
|
||||
{
|
||||
String result = armor.getItem().getArmorTexture(armor, entity, slot, layer);
|
||||
return result != null ? result : _default;
|
||||
}
|
||||
|
||||
public static boolean renderEntityItem(EntityItem entity, ItemStack item, float bobing, float rotation, Random random, TextureManager engine, RenderBlocks renderBlocks)
|
||||
{
|
||||
IItemRenderer customRenderer = MinecraftForgeClient.getItemRenderer(item, ENTITY);
|
||||
if (customRenderer == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (customRenderer.shouldUseRenderHelper(ENTITY, item, ENTITY_ROTATION))
|
||||
{
|
||||
GL11.glRotatef(rotation, 0.0F, 1.0F, 0.0F);
|
||||
}
|
||||
if (!customRenderer.shouldUseRenderHelper(ENTITY, item, ENTITY_BOBBING))
|
||||
{
|
||||
GL11.glTranslatef(0.0F, -bobing, 0.0F);
|
||||
}
|
||||
boolean is3D = customRenderer.shouldUseRenderHelper(ENTITY, item, BLOCK_3D);
|
||||
|
||||
engine.func_110577_a(item.getItemSpriteNumber() == 0 ? TextureMap.field_110575_b : TextureMap.field_110576_c);
|
||||
Block block = (item.itemID < Block.blocksList.length ? Block.blocksList[item.itemID] : null);
|
||||
if (is3D || (block != null && RenderBlocks.renderItemIn3d(block.getRenderType())))
|
||||
{
|
||||
int renderType = (block != null ? block.getRenderType() : 1);
|
||||
float scale = (renderType == 1 || renderType == 19 || renderType == 12 || renderType == 2 ? 0.5F : 0.25F);
|
||||
|
||||
if (RenderItem.renderInFrame)
|
||||
{
|
||||
GL11.glScalef(1.25F, 1.25F, 1.25F);
|
||||
GL11.glTranslatef(0.0F, 0.05F, 0.0F);
|
||||
GL11.glRotatef(-90.0F, 0.0F, 1.0F, 0.0F);
|
||||
}
|
||||
|
||||
GL11.glScalef(scale, scale, scale);
|
||||
|
||||
int size = item.stackSize;
|
||||
int count = (size > 40 ? 5 : (size > 20 ? 4 : (size > 5 ? 3 : (size > 1 ? 2 : 1))));
|
||||
|
||||
for(int j = 0; j < count; j++)
|
||||
{
|
||||
GL11.glPushMatrix();
|
||||
if (j > 0)
|
||||
{
|
||||
GL11.glTranslatef(
|
||||
((random.nextFloat() * 2.0F - 1.0F) * 0.2F) / scale,
|
||||
((random.nextFloat() * 2.0F - 1.0F) * 0.2F) / scale,
|
||||
((random.nextFloat() * 2.0F - 1.0F) * 0.2F) / scale);
|
||||
}
|
||||
customRenderer.renderItem(ENTITY, item, renderBlocks, entity);
|
||||
GL11.glPopMatrix();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GL11.glScalef(0.5F, 0.5F, 0.5F);
|
||||
customRenderer.renderItem(ENTITY, item, renderBlocks, entity);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean renderInventoryItem(RenderBlocks renderBlocks, TextureManager engine, ItemStack item, boolean inColor, float zLevel, float x, float y)
|
||||
{
|
||||
IItemRenderer customRenderer = MinecraftForgeClient.getItemRenderer(item, INVENTORY);
|
||||
if (customRenderer == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
engine.func_110577_a(item.getItemSpriteNumber() == 0 ? TextureMap.field_110575_b : TextureMap.field_110576_c);
|
||||
if (customRenderer.shouldUseRenderHelper(INVENTORY, item, INVENTORY_BLOCK))
|
||||
{
|
||||
GL11.glPushMatrix();
|
||||
GL11.glTranslatef(x - 2, y + 3, -3.0F + zLevel);
|
||||
GL11.glScalef(10F, 10F, 10F);
|
||||
GL11.glTranslatef(1.0F, 0.5F, 1.0F);
|
||||
GL11.glScalef(1.0F, 1.0F, -1F);
|
||||
GL11.glRotatef(210F, 1.0F, 0.0F, 0.0F);
|
||||
GL11.glRotatef(45F, 0.0F, 1.0F, 0.0F);
|
||||
|
||||
if(inColor)
|
||||
{
|
||||
int color = Item.itemsList[item.itemID].getColorFromItemStack(item, 0);
|
||||
float r = (float)(color >> 16 & 0xff) / 255F;
|
||||
float g = (float)(color >> 8 & 0xff) / 255F;
|
||||
float b = (float)(color & 0xff) / 255F;
|
||||
GL11.glColor4f(r, g, b, 1.0F);
|
||||
}
|
||||
|
||||
GL11.glRotatef(-90F, 0.0F, 1.0F, 0.0F);
|
||||
renderBlocks.useInventoryTint = inColor;
|
||||
customRenderer.renderItem(INVENTORY, item, renderBlocks);
|
||||
renderBlocks.useInventoryTint = true;
|
||||
GL11.glPopMatrix();
|
||||
}
|
||||
else
|
||||
{
|
||||
GL11.glDisable(GL11.GL_LIGHTING);
|
||||
GL11.glPushMatrix();
|
||||
GL11.glTranslatef(x, y, -3.0F + zLevel);
|
||||
|
||||
if (inColor)
|
||||
{
|
||||
int color = Item.itemsList[item.itemID].getColorFromItemStack(item, 0);
|
||||
float r = (float)(color >> 16 & 255) / 255.0F;
|
||||
float g = (float)(color >> 8 & 255) / 255.0F;
|
||||
float b = (float)(color & 255) / 255.0F;
|
||||
GL11.glColor4f(r, g, b, 1.0F);
|
||||
}
|
||||
|
||||
customRenderer.renderItem(INVENTORY, item, renderBlocks);
|
||||
GL11.glPopMatrix();
|
||||
GL11.glEnable(GL11.GL_LIGHTING);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void renderEquippedItem(ItemRenderType type, IItemRenderer customRenderer, RenderBlocks renderBlocks, EntityLivingBase entity, ItemStack item)
|
||||
{
|
||||
if (customRenderer.shouldUseRenderHelper(type, item, EQUIPPED_BLOCK))
|
||||
{
|
||||
GL11.glPushMatrix();
|
||||
GL11.glTranslatef(-0.5F, -0.5F, -0.5F);
|
||||
customRenderer.renderItem(type, item, renderBlocks, entity);
|
||||
GL11.glPopMatrix();
|
||||
}
|
||||
else
|
||||
{
|
||||
GL11.glPushMatrix();
|
||||
GL11.glEnable(GL12.GL_RESCALE_NORMAL);
|
||||
GL11.glTranslatef(0.0F, -0.3F, 0.0F);
|
||||
GL11.glScalef(1.5F, 1.5F, 1.5F);
|
||||
GL11.glRotatef(50.0F, 0.0F, 1.0F, 0.0F);
|
||||
GL11.glRotatef(335.0F, 0.0F, 0.0F, 1.0F);
|
||||
GL11.glTranslatef(-0.9375F, -0.0625F, 0.0F);
|
||||
customRenderer.renderItem(type, item, renderBlocks, entity);
|
||||
GL11.glDisable(GL12.GL_RESCALE_NORMAL);
|
||||
GL11.glPopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
//Optifine Helper Functions u.u, these are here specifically for Optifine
|
||||
//Note: When using Optfine, these methods are invoked using reflection, which
|
||||
//incurs a major performance penalty.
|
||||
public static void orientBedCamera(Minecraft mc, EntityLivingBase entity)
|
||||
{
|
||||
int x = MathHelper.floor_double(entity.posX);
|
||||
int y = MathHelper.floor_double(entity.posY);
|
||||
int z = MathHelper.floor_double(entity.posZ);
|
||||
Block block = Block.blocksList[mc.theWorld.getBlockId(x, y, z)];
|
||||
|
||||
if (block != null && block.isBed(mc.theWorld, x, y, z, entity))
|
||||
{
|
||||
int var12 = block.getBedDirection(mc.theWorld, x, y, z);
|
||||
GL11.glRotatef((float)(var12 * 90), 0.0F, 1.0F, 0.0F);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean onDrawBlockHighlight(RenderGlobal context, EntityPlayer player, MovingObjectPosition target, int subID, ItemStack currentItem, float partialTicks)
|
||||
{
|
||||
return MinecraftForge.EVENT_BUS.post(new DrawBlockHighlightEvent(context, player, target, subID, currentItem, partialTicks));
|
||||
}
|
||||
|
||||
public static void dispatchRenderLast(RenderGlobal context, float partialTicks)
|
||||
{
|
||||
MinecraftForge.EVENT_BUS.post(new RenderWorldLastEvent(context, partialTicks));
|
||||
}
|
||||
|
||||
public static void onTextureStitchedPre(TextureMap map)
|
||||
{
|
||||
MinecraftForge.EVENT_BUS.post(new TextureStitchEvent.Pre(map));
|
||||
}
|
||||
|
||||
public static void onTextureStitchedPost(TextureMap map)
|
||||
{
|
||||
MinecraftForge.EVENT_BUS.post(new TextureStitchEvent.Post(map));
|
||||
}
|
||||
|
||||
/**
|
||||
* This is added for Optifine's convenience. And to explode if a ModMaker is developing.
|
||||
* @param texture
|
||||
*/
|
||||
public static void onTextureLoadPre(String texture)
|
||||
{
|
||||
if (Tessellator.renderingWorldRenderer)
|
||||
{
|
||||
String msg = String.format("Warning: Texture %s not preloaded, will cause render glitches!", texture);
|
||||
System.out.println(msg);
|
||||
if (Tessellator.class.getPackage() != null)
|
||||
{
|
||||
if (Tessellator.class.getPackage().getName().startsWith("net.minecraft."))
|
||||
{
|
||||
Minecraft mc = FMLClientHandler.instance().getClient();
|
||||
if (mc.ingameGUI != null)
|
||||
{
|
||||
mc.ingameGUI.getChatGUI().printChatMessage(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int renderPass = -1;
|
||||
public static void setRenderPass(int pass)
|
||||
{
|
||||
renderPass = pass;
|
||||
}
|
||||
|
||||
public static ModelBiped getArmorModel(EntityLivingBase entityLiving, ItemStack itemStack, int slotID, ModelBiped _default)
|
||||
{
|
||||
ModelBiped modelbiped = itemStack.getItem().getArmorModel(entityLiving, itemStack, slotID);
|
||||
return modelbiped == null ? _default : modelbiped;
|
||||
}
|
||||
|
||||
static int stencilBits = 0;
|
||||
public static void createDisplay() throws LWJGLException
|
||||
{
|
||||
ImageIO.setUseCache(false); //Disable on-disc stream cache should speed up texture pack reloading.
|
||||
PixelFormat format = new PixelFormat().withDepthBits(24);
|
||||
try
|
||||
{
|
||||
//TODO: Figure out how to determine the max bits.
|
||||
Display.create(format.withStencilBits(8));
|
||||
stencilBits = 8;
|
||||
}
|
||||
catch(LWJGLException e)
|
||||
{
|
||||
Display.create(format);
|
||||
stencilBits = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
package net.minecraftforge.client;
|
||||
|
||||
import org.lwjgl.input.Mouse;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.settings.GameSettings;
|
||||
import net.minecraft.client.gui.GuiControls;
|
||||
import net.minecraft.client.gui.GuiSlot;
|
||||
import net.minecraft.client.settings.KeyBinding;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.texture.TextureMap;
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
|
||||
public class GuiControlsScrollPanel extends GuiSlot
|
||||
{
|
||||
private GuiControls controls;
|
||||
private GameSettings options;
|
||||
private Minecraft mc;
|
||||
private String[] message;
|
||||
private int _mouseX;
|
||||
private int _mouseY;
|
||||
private int selected = -1;
|
||||
|
||||
public GuiControlsScrollPanel(GuiControls controls, GameSettings options, Minecraft mc)
|
||||
{
|
||||
super(mc, controls.width, controls.height, 16, (controls.height - 32) + 4, 25);
|
||||
this.controls = controls;
|
||||
this.options = options;
|
||||
this.mc = mc;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getSize()
|
||||
{
|
||||
return options.keyBindings.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void elementClicked(int i, boolean flag)
|
||||
{
|
||||
if (!flag)
|
||||
{
|
||||
if (selected == -1)
|
||||
{
|
||||
selected = i;
|
||||
}
|
||||
else
|
||||
{
|
||||
options.setKeyBinding(selected, -100);
|
||||
selected = -1;
|
||||
KeyBinding.resetKeyBindingArrayAndHash();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isSelected(int i)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void func_130003_c() {}
|
||||
|
||||
@Override
|
||||
public void drawScreen(int mX, int mY, float f)
|
||||
{
|
||||
_mouseX = mX;
|
||||
_mouseY = mY;
|
||||
|
||||
if (selected != -1 && !Mouse.isButtonDown(0) && Mouse.getDWheel() == 0)
|
||||
{
|
||||
if (Mouse.next() && Mouse.getEventButtonState())
|
||||
{
|
||||
System.out.println(Mouse.getEventButton());
|
||||
options.setKeyBinding(selected, -100 + Mouse.getEventButton());
|
||||
selected = -1;
|
||||
KeyBinding.resetKeyBindingArrayAndHash();
|
||||
}
|
||||
}
|
||||
|
||||
super.drawScreen(mX, mY, f);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawSlot(int index, int xPosition, int yPosition, int l, Tessellator tessellator)
|
||||
{
|
||||
int width = 70;
|
||||
int height = 20;
|
||||
xPosition -= 20;
|
||||
boolean flag = _mouseX >= xPosition && _mouseY >= yPosition && _mouseX < xPosition + width && _mouseY < yPosition + height;
|
||||
int k = (flag ? 2 : 1);
|
||||
|
||||
mc.renderEngine.func_110577_a(TextureMap.field_110576_c);
|
||||
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
controls.drawTexturedModalRect(xPosition, yPosition, 0, 46 + k * 20, width / 2, height);
|
||||
controls.drawTexturedModalRect(xPosition + width / 2, yPosition, 200 - width / 2, 46 + k * 20, width / 2, height);
|
||||
controls.drawString(mc.fontRenderer, options.getKeyBindingDescription(index), xPosition + width + 4, yPosition + 6, 0xFFFFFFFF);
|
||||
|
||||
boolean conflict = false;
|
||||
for (int x = 0; x < options.keyBindings.length; x++)
|
||||
{
|
||||
if (x != index && options.keyBindings[x].keyCode == options.keyBindings[index].keyCode)
|
||||
{
|
||||
conflict = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
String str = (conflict ? EnumChatFormatting.RED : "") + options.getOptionDisplayString(index);
|
||||
str = (index == selected ? EnumChatFormatting.WHITE + "> " + EnumChatFormatting.YELLOW + "??? " + EnumChatFormatting.WHITE + "<" : str);
|
||||
controls.drawCenteredString(mc.fontRenderer, str, xPosition + (width / 2), yPosition + (height - 8) / 2, 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
public boolean keyTyped(char c, int i)
|
||||
{
|
||||
if (selected != -1)
|
||||
{
|
||||
options.setKeyBinding(selected, i);
|
||||
selected = -1;
|
||||
KeyBinding.resetKeyBindingArrayAndHash();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,871 +0,0 @@
|
||||
package net.minecraftforge.client;
|
||||
|
||||
import static net.minecraftforge.client.event.RenderGameOverlayEvent.ElementType.*;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.gui.Gui;
|
||||
import net.minecraft.client.gui.GuiIngame;
|
||||
import net.minecraft.client.gui.GuiPlayerInfo;
|
||||
import net.minecraft.client.gui.ScaledResolution;
|
||||
import net.minecraft.client.multiplayer.NetClientHandler;
|
||||
import net.minecraft.client.renderer.RenderHelper;
|
||||
import net.minecraft.client.renderer.texture.TextureMap;
|
||||
import net.minecraft.client.resources.ResourceLocation;
|
||||
import net.minecraft.crash.CallableMinecraftVersion;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.entity.ai.attributes.AttributeInstance;
|
||||
import net.minecraft.entity.player.InventoryPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.potion.Potion;
|
||||
import net.minecraft.scoreboard.Score;
|
||||
import net.minecraft.scoreboard.ScoreObjective;
|
||||
import net.minecraft.scoreboard.ScorePlayerTeam;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
import net.minecraft.util.FoodStats;
|
||||
import net.minecraft.util.MathHelper;
|
||||
import net.minecraft.util.StatCollector;
|
||||
import net.minecraft.util.StringUtils;
|
||||
import net.minecraft.world.EnumSkyBlock;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
import net.minecraftforge.client.event.RenderGameOverlayEvent;
|
||||
import net.minecraftforge.client.event.RenderGameOverlayEvent.ElementType;
|
||||
import net.minecraftforge.common.ForgeHooks;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
|
||||
import cpw.mods.fml.common.FMLCommonHandler;
|
||||
|
||||
public class GuiIngameForge extends GuiIngame
|
||||
{
|
||||
private static final ResourceLocation VIGNETTE = new ResourceLocation("textures/misc/vignette.png");
|
||||
private static final ResourceLocation WIDGITS = new ResourceLocation("textures/gui/widgets.png");
|
||||
private static final ResourceLocation PUMPKIN_BLUR = new ResourceLocation("textures/misc/pumpkinblur.png");
|
||||
|
||||
private static final int WHITE = 0xFFFFFF;
|
||||
|
||||
//Flags to toggle the rendering of certain aspects of the HUD, valid conditions
|
||||
//must be met for them to render normally. If those conditions are met, but this flag
|
||||
//is false, they will not be rendered.
|
||||
public static boolean renderHelmet = true;
|
||||
public static boolean renderPortal = true;
|
||||
public static boolean renderHotbar = true;
|
||||
public static boolean renderCrosshairs = true;
|
||||
public static boolean renderBossHealth = true;
|
||||
public static boolean renderHealth = true;
|
||||
public static boolean renderArmor = true;
|
||||
public static boolean renderFood = true;
|
||||
public static boolean renderHealthMount = true;
|
||||
public static boolean renderAir = true;
|
||||
public static boolean renderExperiance = true;
|
||||
public static boolean renderJumpBar = true;
|
||||
public static boolean renderObjective = true;
|
||||
|
||||
public static int left_height = 39;
|
||||
public static int right_height = 39;
|
||||
|
||||
private ScaledResolution res = null;
|
||||
private FontRenderer fontrenderer = null;
|
||||
private RenderGameOverlayEvent eventParent;
|
||||
private static final String MC_VERSION = (new CallableMinecraftVersion(null)).minecraftVersion();
|
||||
|
||||
public GuiIngameForge(Minecraft mc)
|
||||
{
|
||||
super(mc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderGameOverlay(float partialTicks, boolean hasScreen, int mouseX, int mouseY)
|
||||
{
|
||||
res = new ScaledResolution(mc.gameSettings, mc.displayWidth, mc.displayHeight);
|
||||
eventParent = new RenderGameOverlayEvent(partialTicks, res, mouseX, mouseY);
|
||||
int width = res.getScaledWidth();
|
||||
int height = res.getScaledHeight();
|
||||
renderHealthMount = mc.thePlayer.ridingEntity instanceof EntityLivingBase;
|
||||
renderFood = mc.thePlayer.ridingEntity == null;
|
||||
renderJumpBar = mc.thePlayer.func_110317_t();
|
||||
|
||||
right_height = 39;
|
||||
left_height = 39;
|
||||
|
||||
if (pre(ALL)) return;
|
||||
|
||||
fontrenderer = mc.fontRenderer;
|
||||
mc.entityRenderer.setupOverlayRendering();
|
||||
GL11.glEnable(GL11.GL_BLEND);
|
||||
|
||||
if (Minecraft.isFancyGraphicsEnabled())
|
||||
{
|
||||
renderVignette(mc.thePlayer.getBrightness(partialTicks), width, height);
|
||||
}
|
||||
else
|
||||
{
|
||||
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
|
||||
if (renderHelmet) renderHelmet(res, partialTicks, hasScreen, mouseX, mouseY);
|
||||
|
||||
if (renderPortal && !mc.thePlayer.isPotionActive(Potion.confusion))
|
||||
{
|
||||
renderPortal(width, height, partialTicks);
|
||||
}
|
||||
|
||||
if (!mc.playerController.enableEverythingIsScrewedUpMode())
|
||||
{
|
||||
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
zLevel = -90.0F;
|
||||
rand.setSeed((long)(updateCounter * 312871));
|
||||
|
||||
if (renderCrosshairs) renderCrosshairs(width, height);
|
||||
if (renderBossHealth) renderBossHealth();
|
||||
|
||||
if (this.mc.playerController.shouldDrawHUD())
|
||||
{
|
||||
if (renderHealth) renderHealth(width, height);
|
||||
if (renderArmor) renderArmor(width, height);
|
||||
if (renderFood) renderFood(width, height);
|
||||
if (renderHealthMount) renderHealthMount(width, height);
|
||||
if (renderAir) renderAir(width, height);
|
||||
}
|
||||
if (renderHotbar) renderHotbar(width, height, partialTicks);
|
||||
}
|
||||
|
||||
if (renderJumpBar)
|
||||
{
|
||||
renderJumpBar(width, height);
|
||||
}
|
||||
else if (renderExperiance)
|
||||
{
|
||||
renderExperience(width, height);
|
||||
}
|
||||
|
||||
renderSleepFade(width, height);
|
||||
renderToolHightlight(width, height);
|
||||
renderHUDText(width, height);
|
||||
renderRecordOverlay(width, height, partialTicks);
|
||||
|
||||
ScoreObjective objective = mc.theWorld.getScoreboard().func_96539_a(1);
|
||||
if (renderObjective && objective != null)
|
||||
{
|
||||
this.func_96136_a(objective, height, width, fontrenderer);
|
||||
}
|
||||
|
||||
GL11.glEnable(GL11.GL_BLEND);
|
||||
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
GL11.glDisable(GL11.GL_ALPHA_TEST);
|
||||
|
||||
renderChat(width, height);
|
||||
|
||||
renderPlayerList(width, height);
|
||||
|
||||
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
GL11.glDisable(GL11.GL_LIGHTING);
|
||||
GL11.glEnable(GL11.GL_ALPHA_TEST);
|
||||
|
||||
post(ALL);
|
||||
}
|
||||
|
||||
public ScaledResolution getResolution()
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
protected void renderHotbar(int width, int height, float partialTicks)
|
||||
{
|
||||
if (pre(HOTBAR)) return;
|
||||
mc.mcProfiler.startSection("actionBar");
|
||||
|
||||
GL11.glEnable(GL11.GL_BLEND);
|
||||
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
mc.renderEngine.func_110577_a(WIDGITS);
|
||||
|
||||
InventoryPlayer inv = mc.thePlayer.inventory;
|
||||
drawTexturedModalRect(width / 2 - 91, height - 22, 0, 0, 182, 22);
|
||||
drawTexturedModalRect(width / 2 - 91 - 1 + inv.currentItem * 20, height - 22 - 1, 0, 22, 24, 22);
|
||||
|
||||
GL11.glDisable(GL11.GL_BLEND);
|
||||
GL11.glEnable(GL12.GL_RESCALE_NORMAL);
|
||||
RenderHelper.enableGUIStandardItemLighting();
|
||||
|
||||
for (int i = 0; i < 9; ++i)
|
||||
{
|
||||
int x = width / 2 - 90 + i * 20 + 2;
|
||||
int z = height - 16 - 3;
|
||||
renderInventorySlot(i, x, z, partialTicks);
|
||||
}
|
||||
|
||||
RenderHelper.disableStandardItemLighting();
|
||||
GL11.glDisable(GL12.GL_RESCALE_NORMAL);
|
||||
mc.mcProfiler.endSection();
|
||||
post(HOTBAR);
|
||||
}
|
||||
|
||||
protected void renderCrosshairs(int width, int height)
|
||||
{
|
||||
if (pre(CROSSHAIRS)) return;
|
||||
bind(Gui.field_110324_m);
|
||||
GL11.glEnable(GL11.GL_BLEND);
|
||||
GL11.glBlendFunc(GL11.GL_ONE_MINUS_DST_COLOR, GL11.GL_ONE_MINUS_SRC_COLOR);
|
||||
drawTexturedModalRect(width / 2 - 7, height / 2 - 7, 0, 0, 16, 16);
|
||||
GL11.glDisable(GL11.GL_BLEND);
|
||||
post(CROSSHAIRS);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderBossHealth()
|
||||
{
|
||||
if (pre(BOSSHEALTH)) return;
|
||||
mc.mcProfiler.startSection("bossHealth");
|
||||
super.renderBossHealth();
|
||||
mc.mcProfiler.endSection();
|
||||
post(BOSSHEALTH);
|
||||
}
|
||||
|
||||
private void renderHelmet(ScaledResolution res, float partialTicks, boolean hasScreen, int mouseX, int mouseY)
|
||||
{
|
||||
if (pre(HELMET)) return;
|
||||
|
||||
ItemStack itemstack = this.mc.thePlayer.inventory.armorItemInSlot(3);
|
||||
|
||||
if (this.mc.gameSettings.thirdPersonView == 0 && itemstack != null && itemstack.getItem() != null)
|
||||
{
|
||||
if (itemstack.itemID == Block.pumpkin.blockID)
|
||||
{
|
||||
renderPumpkinBlur(res.getScaledWidth(), res.getScaledHeight());
|
||||
}
|
||||
else
|
||||
{
|
||||
itemstack.getItem().renderHelmetOverlay(itemstack, mc.thePlayer, res, partialTicks, hasScreen, mouseX, mouseY);
|
||||
}
|
||||
}
|
||||
|
||||
post(HELMET);
|
||||
}
|
||||
|
||||
protected void renderArmor(int width, int height)
|
||||
{
|
||||
if (pre(ARMOR)) return;
|
||||
mc.mcProfiler.startSection("armor");
|
||||
|
||||
int left = width / 2 - 91;
|
||||
int top = height - left_height;
|
||||
|
||||
int level = ForgeHooks.getTotalArmorValue(mc.thePlayer);
|
||||
for (int i = 1; level > 0 && i < 20; i += 2)
|
||||
{
|
||||
if (i < level)
|
||||
{
|
||||
drawTexturedModalRect(left, top, 34, 9, 9, 9);
|
||||
}
|
||||
else if (i == level)
|
||||
{
|
||||
drawTexturedModalRect(left, top, 25, 9, 9, 9);
|
||||
}
|
||||
else if (i > level)
|
||||
{
|
||||
drawTexturedModalRect(left, top, 16, 9, 9, 9);
|
||||
}
|
||||
left += 8;
|
||||
}
|
||||
left_height += 10;
|
||||
|
||||
mc.mcProfiler.endSection();
|
||||
post(ARMOR);
|
||||
}
|
||||
|
||||
protected void renderPortal(int width, int height, float partialTicks)
|
||||
{
|
||||
if (pre(PORTAL)) return;
|
||||
|
||||
float f1 = mc.thePlayer.prevTimeInPortal + (mc.thePlayer.timeInPortal - mc.thePlayer.prevTimeInPortal) * partialTicks;
|
||||
|
||||
if (f1 > 0.0F)
|
||||
{
|
||||
func_130015_b(f1, width, height);
|
||||
}
|
||||
|
||||
post(PORTAL);
|
||||
}
|
||||
|
||||
protected void renderAir(int width, int height)
|
||||
{
|
||||
if (pre(AIR)) return;
|
||||
mc.mcProfiler.startSection("air");
|
||||
int left = width / 2 + 91;
|
||||
int top = height - right_height;
|
||||
|
||||
if (mc.thePlayer.isInsideOfMaterial(Material.water))
|
||||
{
|
||||
int air = mc.thePlayer.getAir();
|
||||
int full = MathHelper.ceiling_double_int((double)(air - 2) * 10.0D / 300.0D);
|
||||
int partial = MathHelper.ceiling_double_int((double)air * 10.0D / 300.0D) - full;
|
||||
|
||||
for (int i = 0; i < full + partial; ++i)
|
||||
{
|
||||
drawTexturedModalRect(left - i * 8 - 9, top, (i < full ? 16 : 25), 18, 9, 9);
|
||||
}
|
||||
right_height += 10;
|
||||
}
|
||||
|
||||
mc.mcProfiler.endSection();
|
||||
post(AIR);
|
||||
}
|
||||
|
||||
public void renderHealth(int width, int height)
|
||||
{
|
||||
bind(field_110324_m);
|
||||
if (pre(HEALTH)) return;
|
||||
mc.mcProfiler.startSection("health");
|
||||
|
||||
boolean highlight = mc.thePlayer.hurtResistantTime / 3 % 2 == 1;
|
||||
|
||||
if (mc.thePlayer.hurtResistantTime < 10)
|
||||
{
|
||||
highlight = false;
|
||||
}
|
||||
|
||||
AttributeInstance attrMaxHealth = this.mc.thePlayer.func_110148_a(SharedMonsterAttributes.field_111267_a);
|
||||
int health = MathHelper.ceiling_float_int(mc.thePlayer.func_110143_aJ());
|
||||
int healthLast = MathHelper.ceiling_float_int(mc.thePlayer.prevHealth);
|
||||
float healthMax = (float)attrMaxHealth.func_111126_e();
|
||||
float absorb = this.mc.thePlayer.func_110139_bj();
|
||||
|
||||
int healthRows = MathHelper.ceiling_float_int((healthMax + absorb) / 2.0F / 10.0F);
|
||||
int rowHeight = Math.max(10 - (healthRows - 2), 3);
|
||||
|
||||
this.rand.setSeed((long)(updateCounter * 312871));
|
||||
|
||||
int left = width / 2 - 91;
|
||||
int top = height - left_height;
|
||||
left_height += (healthRows * rowHeight);
|
||||
if (rowHeight != 10) left_height += 10 - rowHeight;
|
||||
|
||||
int regen = -1;
|
||||
if (mc.thePlayer.isPotionActive(Potion.regeneration))
|
||||
{
|
||||
regen = updateCounter % 25;
|
||||
}
|
||||
|
||||
final int TOP = 9 * (mc.theWorld.getWorldInfo().isHardcoreModeEnabled() ? 5 : 0);
|
||||
final int BACKGROUND = (highlight ? 25 : 16);
|
||||
int MARGIN = 16;
|
||||
if (mc.thePlayer.isPotionActive(Potion.poison)) MARGIN += 36;
|
||||
else if (mc.thePlayer.isPotionActive(Potion.wither)) MARGIN += 72;
|
||||
float absorbRemaining = absorb;
|
||||
|
||||
for (int i = MathHelper.ceiling_float_int((healthMax + absorb) / 2.0F) - 1; i >= 0; --i)
|
||||
{
|
||||
int b0 = (highlight ? 1 : 0);
|
||||
int row = MathHelper.ceiling_float_int((float)(i + 1) / 10.0F) - 1;
|
||||
int x = left + i % 10 * 8;
|
||||
int y = top - row * rowHeight;
|
||||
|
||||
if (health <= 4) y += rand.nextInt(2);
|
||||
if (i == regen) y -= 2;
|
||||
|
||||
drawTexturedModalRect(x, y, BACKGROUND, TOP, 9, 9);
|
||||
|
||||
if (highlight)
|
||||
{
|
||||
if (i * 2 + 1 < healthLast)
|
||||
drawTexturedModalRect(x, y, MARGIN + 54, TOP, 9, 9); //6
|
||||
else if (i * 2 + 1 == healthLast)
|
||||
drawTexturedModalRect(x, y, MARGIN + 63, TOP, 9, 9); //7
|
||||
}
|
||||
|
||||
if (absorbRemaining > 0.0F)
|
||||
{
|
||||
if (absorbRemaining == absorb && absorb % 2.0F == 1.0F)
|
||||
drawTexturedModalRect(x, y, MARGIN + 153, TOP, 9, 9); //17
|
||||
else
|
||||
drawTexturedModalRect(x, y, MARGIN + 144, TOP, 9, 9); //16
|
||||
absorbRemaining -= 2.0F;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i * 2 + 1 < health)
|
||||
drawTexturedModalRect(x, y, MARGIN + 36, TOP, 9, 9); //4
|
||||
else if (i * 2 + 1 == health)
|
||||
drawTexturedModalRect(x, y, MARGIN + 45, TOP, 9, 9); //5
|
||||
}
|
||||
}
|
||||
|
||||
mc.mcProfiler.endSection();
|
||||
post(HEALTH);
|
||||
}
|
||||
|
||||
public void renderFood(int width, int height)
|
||||
{
|
||||
if (pre(FOOD)) return;
|
||||
mc.mcProfiler.startSection("food");
|
||||
|
||||
int left = width / 2 + 91;
|
||||
int top = height - right_height;
|
||||
right_height += 10;
|
||||
boolean unused = false;// Unused flag in vanilla, seems to be part of a 'fade out' mechanic
|
||||
|
||||
FoodStats stats = mc.thePlayer.getFoodStats();
|
||||
int level = stats.getFoodLevel();
|
||||
int levelLast = stats.getPrevFoodLevel();
|
||||
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
int idx = i * 2 + 1;
|
||||
int x = left - i * 8 - 9;
|
||||
int y = top;
|
||||
int icon = 16;
|
||||
byte backgound = 0;
|
||||
|
||||
if (mc.thePlayer.isPotionActive(Potion.hunger))
|
||||
{
|
||||
icon += 36;
|
||||
backgound = 13;
|
||||
}
|
||||
if (unused) backgound = 1; //Probably should be a += 1 but vanilla never uses this
|
||||
|
||||
if (mc.thePlayer.getFoodStats().getSaturationLevel() <= 0.0F && updateCounter % (level * 3 + 1) == 0)
|
||||
{
|
||||
y = top + (rand.nextInt(3) - 1);
|
||||
}
|
||||
|
||||
drawTexturedModalRect(x, y, 16 + backgound * 9, 27, 9, 9);
|
||||
|
||||
if (unused)
|
||||
{
|
||||
if (idx < levelLast)
|
||||
drawTexturedModalRect(x, y, icon + 54, 27, 9, 9);
|
||||
else if (idx == levelLast)
|
||||
drawTexturedModalRect(x, y, icon + 63, 27, 9, 9);
|
||||
}
|
||||
|
||||
if (idx < level)
|
||||
drawTexturedModalRect(x, y, icon + 36, 27, 9, 9);
|
||||
else if (idx == level)
|
||||
drawTexturedModalRect(x, y, icon + 45, 27, 9, 9);
|
||||
}
|
||||
mc.mcProfiler.endSection();
|
||||
post(FOOD);
|
||||
}
|
||||
|
||||
protected void renderSleepFade(int width, int height)
|
||||
{
|
||||
if (mc.thePlayer.getSleepTimer() > 0)
|
||||
{
|
||||
mc.mcProfiler.startSection("sleep");
|
||||
GL11.glDisable(GL11.GL_DEPTH_TEST);
|
||||
GL11.glDisable(GL11.GL_ALPHA_TEST);
|
||||
int sleepTime = mc.thePlayer.getSleepTimer();
|
||||
float opacity = (float)sleepTime / 100.0F;
|
||||
|
||||
if (opacity > 1.0F)
|
||||
{
|
||||
opacity = 1.0F - (float)(sleepTime - 100) / 10.0F;
|
||||
}
|
||||
|
||||
int color = (int)(220.0F * opacity) << 24 | 1052704;
|
||||
drawRect(0, 0, width, height, color);
|
||||
GL11.glEnable(GL11.GL_ALPHA_TEST);
|
||||
GL11.glEnable(GL11.GL_DEPTH_TEST);
|
||||
mc.mcProfiler.endSection();
|
||||
}
|
||||
}
|
||||
|
||||
protected void renderExperience(int width, int height)
|
||||
{
|
||||
bind(field_110324_m);
|
||||
if (pre(EXPERIENCE)) return;
|
||||
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
|
||||
if (mc.playerController.func_78763_f())
|
||||
{
|
||||
mc.mcProfiler.startSection("expBar");
|
||||
int cap = this.mc.thePlayer.xpBarCap();
|
||||
int left = width / 2 - 91;
|
||||
|
||||
if (cap > 0)
|
||||
{
|
||||
short barWidth = 182;
|
||||
int filled = (int)(mc.thePlayer.experience * (float)(barWidth + 1));
|
||||
int top = height - 32 + 3;
|
||||
drawTexturedModalRect(left, top, 0, 64, barWidth, 5);
|
||||
|
||||
if (filled > 0)
|
||||
{
|
||||
drawTexturedModalRect(left, top, 0, 69, filled, 5);
|
||||
}
|
||||
}
|
||||
|
||||
this.mc.mcProfiler.endSection();
|
||||
|
||||
|
||||
if (mc.playerController.func_78763_f() && mc.thePlayer.experienceLevel > 0)
|
||||
{
|
||||
mc.mcProfiler.startSection("expLevel");
|
||||
boolean flag1 = false;
|
||||
int color = flag1 ? 16777215 : 8453920;
|
||||
String text = "" + mc.thePlayer.experienceLevel;
|
||||
int x = (width - fontrenderer.getStringWidth(text)) / 2;
|
||||
int y = height - 31 - 4;
|
||||
fontrenderer.drawString(text, x + 1, y, 0);
|
||||
fontrenderer.drawString(text, x - 1, y, 0);
|
||||
fontrenderer.drawString(text, x, y + 1, 0);
|
||||
fontrenderer.drawString(text, x, y - 1, 0);
|
||||
fontrenderer.drawString(text, x, y, color);
|
||||
mc.mcProfiler.endSection();
|
||||
}
|
||||
}
|
||||
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
|
||||
post(EXPERIENCE);
|
||||
}
|
||||
|
||||
protected void renderJumpBar(int width, int height)
|
||||
{
|
||||
bind(field_110324_m);
|
||||
if (pre(JUMPBAR)) return;
|
||||
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
|
||||
mc.mcProfiler.startSection("jumpBar");
|
||||
float charge = mc.thePlayer.func_110319_bJ();
|
||||
final int barWidth = 182;
|
||||
int x = (width / 2) - (barWidth / 2);
|
||||
int filled = (int)(charge * (float)(barWidth + 1));
|
||||
int top = height - 32 + 3;
|
||||
|
||||
drawTexturedModalRect(x, top, 0, 84, barWidth, 5);
|
||||
|
||||
if (filled > 0)
|
||||
{
|
||||
this.drawTexturedModalRect(x, top, 0, 89, filled, 5);
|
||||
}
|
||||
|
||||
mc.mcProfiler.endSection();
|
||||
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
|
||||
post(JUMPBAR);
|
||||
}
|
||||
|
||||
protected void renderToolHightlight(int width, int height)
|
||||
{
|
||||
if (this.mc.gameSettings.heldItemTooltips)
|
||||
{
|
||||
mc.mcProfiler.startSection("toolHighlight");
|
||||
|
||||
if (this.remainingHighlightTicks > 0 && this.highlightingItemStack != null)
|
||||
{
|
||||
String name = this.highlightingItemStack.getDisplayName();
|
||||
|
||||
int opacity = (int)((float)this.remainingHighlightTicks * 256.0F / 10.0F);
|
||||
if (opacity > 255) opacity = 255;
|
||||
|
||||
if (opacity > 0)
|
||||
{
|
||||
int y = height - 59;
|
||||
if (!mc.playerController.shouldDrawHUD()) y += 14;
|
||||
|
||||
GL11.glPushMatrix();
|
||||
GL11.glEnable(GL11.GL_BLEND);
|
||||
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
FontRenderer font = highlightingItemStack.getItem().getFontRenderer(highlightingItemStack);
|
||||
if (font != null)
|
||||
{
|
||||
int x = (width - font.getStringWidth(name)) / 2;
|
||||
font.drawStringWithShadow(name, x, y, WHITE | (opacity << 24));
|
||||
}
|
||||
else
|
||||
{
|
||||
int x = (width - fontrenderer.getStringWidth(name)) / 2;
|
||||
fontrenderer.drawStringWithShadow(name, x, y, WHITE | (opacity << 24));
|
||||
}
|
||||
GL11.glDisable(GL11.GL_BLEND);
|
||||
GL11.glPopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
mc.mcProfiler.endSection();
|
||||
}
|
||||
}
|
||||
|
||||
protected void renderHUDText(int width, int height)
|
||||
{
|
||||
mc.mcProfiler.startSection("forgeHudText");
|
||||
ArrayList<String> left = new ArrayList<String>();
|
||||
ArrayList<String> right = new ArrayList<String>();
|
||||
|
||||
if (mc.isDemo())
|
||||
{
|
||||
long time = mc.theWorld.getTotalWorldTime();
|
||||
if (time >= 120500L)
|
||||
{
|
||||
right.add(StatCollector.translateToLocal("demo.demoExpired"));
|
||||
}
|
||||
else
|
||||
{
|
||||
right.add(String.format(StatCollector.translateToLocal("demo.remainingTime"), StringUtils.ticksToElapsedTime((int)(120500L - time))));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (this.mc.gameSettings.showDebugInfo)
|
||||
{
|
||||
mc.mcProfiler.startSection("debug");
|
||||
GL11.glPushMatrix();
|
||||
left.add("Minecraft " + MC_VERSION + " (" + this.mc.debug + ")");
|
||||
left.add(mc.debugInfoRenders());
|
||||
left.add(mc.getEntityDebug());
|
||||
left.add(mc.debugInfoEntities());
|
||||
left.add(mc.getWorldProviderName());
|
||||
left.add(null); //Spacer
|
||||
|
||||
long max = Runtime.getRuntime().maxMemory();
|
||||
long total = Runtime.getRuntime().totalMemory();
|
||||
long free = Runtime.getRuntime().freeMemory();
|
||||
long used = total - free;
|
||||
|
||||
right.add("Used memory: " + used * 100L / max + "% (" + used / 1024L / 1024L + "MB) of " + max / 1024L / 1024L + "MB");
|
||||
right.add("Allocated memory: " + total * 100L / max + "% (" + total / 1024L / 1024L + "MB)");
|
||||
|
||||
int x = MathHelper.floor_double(mc.thePlayer.posX);
|
||||
int y = MathHelper.floor_double(mc.thePlayer.posY);
|
||||
int z = MathHelper.floor_double(mc.thePlayer.posZ);
|
||||
float yaw = mc.thePlayer.rotationYaw;
|
||||
int heading = MathHelper.floor_double((double)(mc.thePlayer.rotationYaw * 4.0F / 360.0F) + 0.5D) & 3;
|
||||
|
||||
left.add(String.format("x: %.5f (%d) // c: %d (%d)", mc.thePlayer.posX, x, x >> 4, x & 15));
|
||||
left.add(String.format("y: %.3f (feet pos, %.3f eyes pos)", mc.thePlayer.boundingBox.minY, mc.thePlayer.posY));
|
||||
left.add(String.format("z: %.5f (%d) // c: %d (%d)", mc.thePlayer.posZ, z, z >> 4, z & 15));
|
||||
left.add(String.format("f: %d (%s) / %f", heading, Direction.directions[heading], MathHelper.wrapAngleTo180_float(yaw)));
|
||||
|
||||
if (mc.theWorld != null && mc.theWorld.blockExists(x, y, z))
|
||||
{
|
||||
Chunk chunk = this.mc.theWorld.getChunkFromBlockCoords(x, z);
|
||||
left.add(String.format("lc: %d b: %s bl: %d sl: %d rl: %d",
|
||||
chunk.getTopFilledSegment() + 15,
|
||||
chunk.getBiomeGenForWorldCoords(x & 15, z & 15, mc.theWorld.getWorldChunkManager()).biomeName,
|
||||
chunk.getSavedLightValue(EnumSkyBlock.Block, x & 15, y, z & 15),
|
||||
chunk.getSavedLightValue(EnumSkyBlock.Sky, x & 15, y, z & 15),
|
||||
chunk.getBlockLightValue(x & 15, y, z & 15, 0)));
|
||||
}
|
||||
else
|
||||
{
|
||||
left.add(null);
|
||||
}
|
||||
|
||||
left.add(String.format("ws: %.3f, fs: %.3f, g: %b, fl: %d", mc.thePlayer.capabilities.getWalkSpeed(), mc.thePlayer.capabilities.getFlySpeed(), mc.thePlayer.onGround, mc.theWorld.getHeightValue(x, z)));
|
||||
right.add(null);
|
||||
for (String s : FMLCommonHandler.instance().getBrandings().subList(1, FMLCommonHandler.instance().getBrandings().size()))
|
||||
{
|
||||
right.add(s);
|
||||
}
|
||||
GL11.glPopMatrix();
|
||||
mc.mcProfiler.endSection();
|
||||
}
|
||||
|
||||
RenderGameOverlayEvent.Text event = new RenderGameOverlayEvent.Text(eventParent, left, right);
|
||||
if (!MinecraftForge.EVENT_BUS.post(event))
|
||||
{
|
||||
for (int x = 0; x < left.size(); x++)
|
||||
{
|
||||
String msg = left.get(x);
|
||||
if (msg == null) continue;
|
||||
fontrenderer.drawStringWithShadow(msg, 2, 2 + x * 10, WHITE);
|
||||
}
|
||||
|
||||
for (int x = 0; x < right.size(); x++)
|
||||
{
|
||||
String msg = right.get(x);
|
||||
if (msg == null) continue;
|
||||
int w = fontrenderer.getStringWidth(msg);
|
||||
fontrenderer.drawStringWithShadow(msg, width - w - 10, 2 + x * 10, WHITE);
|
||||
}
|
||||
}
|
||||
|
||||
mc.mcProfiler.endSection();
|
||||
post(TEXT);
|
||||
}
|
||||
|
||||
protected void renderRecordOverlay(int width, int height, float partialTicks)
|
||||
{
|
||||
if (recordPlayingUpFor > 0)
|
||||
{
|
||||
mc.mcProfiler.startSection("overlayMessage");
|
||||
float hue = (float)recordPlayingUpFor - partialTicks;
|
||||
int opacity = (int)(hue * 256.0F / 20.0F);
|
||||
if (opacity > 255) opacity = 255;
|
||||
|
||||
if (opacity > 0)
|
||||
{
|
||||
GL11.glPushMatrix();
|
||||
GL11.glTranslatef((float)(width / 2), (float)(height - 48), 0.0F);
|
||||
GL11.glEnable(GL11.GL_BLEND);
|
||||
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
int color = (recordIsPlaying ? Color.HSBtoRGB(hue / 50.0F, 0.7F, 0.6F) & WHITE : WHITE);
|
||||
fontrenderer.drawString(recordPlaying, -fontrenderer.getStringWidth(recordPlaying) / 2, -4, color | (opacity << 24));
|
||||
GL11.glDisable(GL11.GL_BLEND);
|
||||
GL11.glPopMatrix();
|
||||
}
|
||||
|
||||
mc.mcProfiler.endSection();
|
||||
}
|
||||
}
|
||||
|
||||
protected void renderChat(int width, int height)
|
||||
{
|
||||
GL11.glPushMatrix();
|
||||
GL11.glTranslatef(0.0F, (float)(height - 48), 0.0F);
|
||||
mc.mcProfiler.startSection("chat");
|
||||
persistantChatGUI.drawChat(updateCounter);
|
||||
mc.mcProfiler.endSection();
|
||||
GL11.glPopMatrix();
|
||||
}
|
||||
|
||||
protected void renderPlayerList(int width, int height)
|
||||
{
|
||||
ScoreObjective scoreobjective = this.mc.theWorld.getScoreboard().func_96539_a(0);
|
||||
NetClientHandler handler = mc.thePlayer.sendQueue;
|
||||
|
||||
if (mc.gameSettings.keyBindPlayerList.pressed && (!mc.isIntegratedServerRunning() || handler.playerInfoList.size() > 1 || scoreobjective != null))
|
||||
{
|
||||
this.mc.mcProfiler.startSection("playerList");
|
||||
List players = handler.playerInfoList;
|
||||
int maxPlayers = handler.currentServerMaxPlayers;
|
||||
int rows = maxPlayers;
|
||||
int columns = 1;
|
||||
|
||||
for (columns = 1; rows > 20; rows = (maxPlayers + columns - 1) / columns)
|
||||
{
|
||||
columns++;
|
||||
}
|
||||
|
||||
int columnWidth = 300 / columns;
|
||||
|
||||
if (columnWidth > 150)
|
||||
{
|
||||
columnWidth = 150;
|
||||
}
|
||||
|
||||
int left = (width - columns * columnWidth) / 2;
|
||||
byte border = 10;
|
||||
drawRect(left - 1, border - 1, left + columnWidth * columns, border + 9 * rows, Integer.MIN_VALUE);
|
||||
|
||||
for (int i = 0; i < maxPlayers; i++)
|
||||
{
|
||||
int xPos = left + i % columns * columnWidth;
|
||||
int yPos = border + i / columns * 9;
|
||||
drawRect(xPos, yPos, xPos + columnWidth - 1, yPos + 8, 553648127);
|
||||
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
GL11.glEnable(GL11.GL_ALPHA_TEST);
|
||||
|
||||
if (i < players.size())
|
||||
{
|
||||
GuiPlayerInfo player = (GuiPlayerInfo)players.get(i);
|
||||
ScorePlayerTeam team = mc.theWorld.getScoreboard().getPlayersTeam(player.name);
|
||||
String displayName = ScorePlayerTeam.func_96667_a(team, player.name);
|
||||
fontrenderer.drawStringWithShadow(displayName, xPos, yPos, 16777215);
|
||||
|
||||
if (scoreobjective != null)
|
||||
{
|
||||
int endX = xPos + fontrenderer.getStringWidth(displayName) + 5;
|
||||
int maxX = xPos + columnWidth - 12 - 5;
|
||||
|
||||
if (maxX - endX > 5)
|
||||
{
|
||||
Score score = scoreobjective.getScoreboard().func_96529_a(player.name, scoreobjective);
|
||||
String scoreDisplay = EnumChatFormatting.YELLOW + "" + score.func_96652_c();
|
||||
fontrenderer.drawStringWithShadow(scoreDisplay, maxX - fontrenderer.getStringWidth(scoreDisplay), yPos, 16777215);
|
||||
}
|
||||
}
|
||||
|
||||
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
|
||||
mc.func_110434_K().func_110577_a(Gui.field_110324_m);
|
||||
int pingIndex = 4;
|
||||
int ping = player.responseTime;
|
||||
if (ping < 0) pingIndex = 5;
|
||||
else if (ping < 150) pingIndex = 0;
|
||||
else if (ping < 300) pingIndex = 1;
|
||||
else if (ping < 600) pingIndex = 2;
|
||||
else if (ping < 1000) pingIndex = 3;
|
||||
|
||||
zLevel += 100.0F;
|
||||
drawTexturedModalRect(xPos + columnWidth - 12, yPos, 0, 176 + pingIndex * 8, 10, 8);
|
||||
zLevel -= 100.0F;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void renderHealthMount(int width, int height)
|
||||
{
|
||||
Entity tmp = mc.thePlayer.ridingEntity;
|
||||
if (!(tmp instanceof EntityLivingBase)) return;
|
||||
|
||||
bind(field_110324_m);
|
||||
|
||||
if (pre(HEALTHMOUNT)) return;
|
||||
|
||||
boolean unused = false;
|
||||
int left_align = width / 2 + 91;
|
||||
|
||||
mc.mcProfiler.endStartSection("mountHealth");
|
||||
EntityLivingBase mount = (EntityLivingBase)tmp;
|
||||
int health = (int)Math.ceil((double)mount.func_110143_aJ());
|
||||
float healthMax = mount.func_110138_aP();
|
||||
int hearts = (int)(healthMax + 0.5F) / 2;
|
||||
|
||||
if (hearts > 30) hearts = 30;
|
||||
|
||||
final int MARGIN = 52;
|
||||
final int BACKGROUND = MARGIN + (unused ? 1 : 0);
|
||||
final int HALF = MARGIN + 45;
|
||||
final int FULL = MARGIN + 36;
|
||||
|
||||
for (int heart = 0; hearts > 0; heart += 20)
|
||||
{
|
||||
int top = height - right_height;
|
||||
|
||||
int rowCount = Math.min(hearts, 10);
|
||||
hearts -= rowCount;
|
||||
|
||||
for (int i = 0; i < rowCount; ++i)
|
||||
{
|
||||
int x = left_align - i * 8 - 9;
|
||||
drawTexturedModalRect(x, top, BACKGROUND, 9, 9, 9);
|
||||
|
||||
if (i * 2 + 1 + heart < health)
|
||||
drawTexturedModalRect(x, top, FULL, 9, 9, 9);
|
||||
else if (i * 2 + 1 + heart == health)
|
||||
drawTexturedModalRect(x, top, HALF, 9, 9, 9);
|
||||
}
|
||||
|
||||
right_height += 10;
|
||||
}
|
||||
post(HEALTHMOUNT);
|
||||
}
|
||||
|
||||
//Helper macros
|
||||
private boolean pre(ElementType type)
|
||||
{
|
||||
return MinecraftForge.EVENT_BUS.post(new RenderGameOverlayEvent.Pre(eventParent, type));
|
||||
}
|
||||
private void post(ElementType type)
|
||||
{
|
||||
MinecraftForge.EVENT_BUS.post(new RenderGameOverlayEvent.Post(eventParent, type));
|
||||
}
|
||||
private void bind(ResourceLocation res)
|
||||
{
|
||||
mc.func_110434_K().func_110577_a(res);
|
||||
}
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
package net.minecraftforge.client;
|
||||
|
||||
import net.minecraft.entity.item.EntityItem;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.client.renderer.RenderBlocks;
|
||||
|
||||
public interface IItemRenderer
|
||||
{
|
||||
public enum ItemRenderType
|
||||
{
|
||||
/**
|
||||
* Called to render an in-world item, e.g. one that has been thrown or
|
||||
* dropped. The appropriate OpenGL transformations and scaling have already
|
||||
* been applied, so Tessellator location (0,0,0) is the center of the
|
||||
* EntityItem.
|
||||
*
|
||||
* Data parameters:
|
||||
* RenderBlocks render - The RenderBlocks instance
|
||||
* EntityItem entity - The in-world item to be rendered
|
||||
*/
|
||||
ENTITY,
|
||||
|
||||
/**
|
||||
* Called to render an item currently held in-hand by a living entity. If
|
||||
* rendering as a 3D block, the item will be rotated to a 45-degree angle.
|
||||
* To render a 2D texture with some thickness (like default items), see
|
||||
* net.minecraft.src.ItemRenderer. In either case, rendering should be done
|
||||
* in local coordinates from (0,0,0)-(1,1,1).
|
||||
*
|
||||
* Data parameters:
|
||||
* RenderBlocks render - The RenderBlocks instance
|
||||
* EntityLiving entity - The entity holding this item
|
||||
*/
|
||||
EQUIPPED,
|
||||
|
||||
/**
|
||||
* Called to render an item currently held in-hand by a living entity in
|
||||
* first person. If rendering as a 3D block, the item will be rotated to a
|
||||
* 45-degree angle. To render a 2D texture with some thickness, see
|
||||
* net.minecraft.src.ItemRenderer. In either case, rendering should be done
|
||||
* in local coordinates from (0,0,0)-(1,1,1).
|
||||
*
|
||||
* Data parameters:
|
||||
* RenderBlocks render - The RenderBlocks instance
|
||||
* EntityLiving entity - The entity holding this item
|
||||
*/
|
||||
EQUIPPED_FIRST_PERSON,
|
||||
|
||||
/**
|
||||
* Called to render an item in a GUI inventory slot. If rendering as a 3D
|
||||
* block, the appropriate OpenGL translations and scaling have already been
|
||||
* applied, and the rendering should be done in local coordinates from
|
||||
* (0,0,0)-(1,1,1). If rendering as a 2D texture, the rendering should be in
|
||||
* GUI pixel coordinates from (0, 0, 0)-(16, 16, 0).
|
||||
*
|
||||
* Data parameters:
|
||||
* RenderBlocks render - The RenderBlocks instance
|
||||
*/
|
||||
INVENTORY,
|
||||
|
||||
/**
|
||||
* The render type used for when a ItemMap is rendered in first person,
|
||||
* All appropriate rotations have been applied, and the player's hands,
|
||||
* and the map BG are already rendered.
|
||||
*
|
||||
* Data Parameters:
|
||||
* EntityPlayer player - The player holding the map
|
||||
* RenderEngine engine - The RenderEngine instance
|
||||
* MapData mapData - The map data
|
||||
*/
|
||||
FIRST_PERSON_MAP
|
||||
}
|
||||
|
||||
public enum ItemRendererHelper
|
||||
{
|
||||
/**
|
||||
* Determines if a rotation effect should be used when rendering an
|
||||
* EntityItem, like most default blocks do.
|
||||
*/
|
||||
ENTITY_ROTATION,
|
||||
|
||||
/**
|
||||
* Determines if an up-and-down bobbing effect should be used when
|
||||
* rendering an EntityItem, like most default items do.
|
||||
*/
|
||||
ENTITY_BOBBING,
|
||||
|
||||
/**
|
||||
* Determines if the currently equipped item should be rendered as a 3D
|
||||
* block or as a 2D texture.
|
||||
*/
|
||||
EQUIPPED_BLOCK,
|
||||
|
||||
/**
|
||||
* Determines if the item should equate to a block that has
|
||||
* RenderBlocks.renderItemIn3d return true
|
||||
*/
|
||||
BLOCK_3D,
|
||||
|
||||
/**
|
||||
* Determines if the item should be rendered in GUI inventory slots as a 3D
|
||||
* block or as a 2D texture.
|
||||
*/
|
||||
INVENTORY_BLOCK
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this renderer should handle a specific item's render type
|
||||
* @param item The item we are trying to render
|
||||
* @param type A render type to check if this renderer handles
|
||||
* @return true if this renderer should handle the given render type,
|
||||
* otherwise false
|
||||
*/
|
||||
public boolean handleRenderType(ItemStack item, ItemRenderType type);
|
||||
|
||||
/**
|
||||
* Checks if certain helper functionality should be executed for this renderer.
|
||||
* See ItemRendererHelper for more info
|
||||
*
|
||||
* @param type The render type
|
||||
* @param item The ItemStack being rendered
|
||||
* @param helper The type of helper functionality to be ran
|
||||
* @return True to run the helper functionality, false to not.
|
||||
*/
|
||||
public boolean shouldUseRenderHelper(ItemRenderType type, ItemStack item, ItemRendererHelper helper);
|
||||
|
||||
/**
|
||||
* Called to do the actual rendering, see ItemRenderType for details on when specific
|
||||
* types are run, and what extra data is passed into the data parameter.
|
||||
*
|
||||
* @param type The render type
|
||||
* @param item The ItemStack being rendered
|
||||
* @param data Extra Type specific data
|
||||
*/
|
||||
public void renderItem(ItemRenderType type, ItemStack item, Object... data);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
/*
|
||||
* This software is provided under the terms of the Minecraft Forge Public
|
||||
* License v1.0.
|
||||
*/
|
||||
package net.minecraftforge.client;
|
||||
|
||||
public interface IRenderContextHandler
|
||||
{
|
||||
/** Run before the specified rendering context.
|
||||
*/
|
||||
public void beforeRenderContext();
|
||||
|
||||
/** Run after the specified rendering context.
|
||||
*/
|
||||
public void afterRenderContext();
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
package net.minecraftforge.client;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.WorldClient;
|
||||
|
||||
public abstract class IRenderHandler
|
||||
{
|
||||
@SideOnly(Side.CLIENT)
|
||||
public abstract void render(float partialTicks, WorldClient world, Minecraft mc);
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
/**
|
||||
* This software is provided under the terms of the Minecraft Forge Public
|
||||
* License v1.0.
|
||||
*/
|
||||
|
||||
package net.minecraftforge.client;
|
||||
|
||||
import java.util.BitSet;
|
||||
|
||||
import org.lwjgl.opengl.Display;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.client.renderer.RenderBlocks;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.client.IItemRenderer.ItemRenderType;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
|
||||
public class MinecraftForgeClient
|
||||
{
|
||||
private static IItemRenderer[] customItemRenderers = new IItemRenderer[Item.itemsList.length];
|
||||
|
||||
/**
|
||||
* Register a custom renderer for a specific item. This can be used to
|
||||
* render the item in-world as an EntityItem, when the item is equipped, or
|
||||
* when the item is in an inventory slot.
|
||||
* @param itemID The item ID (shifted index) to handle rendering.
|
||||
* @param renderer The IItemRenderer interface that handles rendering for
|
||||
* this item.
|
||||
*/
|
||||
public static void registerItemRenderer(int itemID, IItemRenderer renderer)
|
||||
{
|
||||
customItemRenderers[itemID] = renderer;
|
||||
}
|
||||
|
||||
public static IItemRenderer getItemRenderer(ItemStack item, ItemRenderType type)
|
||||
{
|
||||
IItemRenderer renderer = customItemRenderers[item.itemID];
|
||||
if (renderer != null && renderer.handleRenderType(item, type))
|
||||
{
|
||||
return customItemRenderers[item.itemID];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static int getRenderPass()
|
||||
{
|
||||
return ForgeHooksClient.renderPass;
|
||||
}
|
||||
|
||||
public static int getStencilBits()
|
||||
{
|
||||
return ForgeHooksClient.stencilBits;
|
||||
}
|
||||
|
||||
|
||||
private static BitSet stencilBits = new BitSet(getStencilBits());
|
||||
static
|
||||
{
|
||||
stencilBits.set(0,getStencilBits());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reserve a stencil bit for use in rendering
|
||||
*
|
||||
* @return A bit or -1 if no further stencil bits are available
|
||||
*/
|
||||
public static int reserveStencilBit()
|
||||
{
|
||||
int bit = stencilBits.nextSetBit(0);
|
||||
if (bit >= 0)
|
||||
{
|
||||
stencilBits.clear(bit);
|
||||
}
|
||||
return bit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the stencil bit for other use
|
||||
*
|
||||
* @param bit The bit from {@link #reserveStencilBit()}
|
||||
*/
|
||||
public static void releaseStencilBit(int bit)
|
||||
{
|
||||
if (bit >= 0 && bit < getStencilBits())
|
||||
{
|
||||
stencilBits.set(bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package net.minecraftforge.client.event;
|
||||
|
||||
import net.minecraftforge.event.Cancelable;
|
||||
import net.minecraftforge.event.Event;
|
||||
|
||||
@Cancelable
|
||||
public class ClientChatReceivedEvent extends Event
|
||||
{
|
||||
public String message;
|
||||
public ClientChatReceivedEvent(String message)
|
||||
{
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package net.minecraftforge.client.event;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.MovingObjectPosition;
|
||||
import net.minecraft.client.renderer.RenderGlobal;
|
||||
import net.minecraftforge.event.Cancelable;
|
||||
import net.minecraftforge.event.Event;
|
||||
|
||||
@Cancelable
|
||||
public class DrawBlockHighlightEvent extends Event
|
||||
{
|
||||
public final RenderGlobal context;
|
||||
public final EntityPlayer player;
|
||||
public final MovingObjectPosition target;
|
||||
public final int subID;
|
||||
public final ItemStack currentItem;
|
||||
public final float partialTicks;
|
||||
|
||||
public DrawBlockHighlightEvent(RenderGlobal context, EntityPlayer player, MovingObjectPosition target, int subID, ItemStack currentItem, float partialTicks)
|
||||
{
|
||||
this.context = context;
|
||||
this.player = player;
|
||||
this.target = target;
|
||||
this.subID = subID;
|
||||
this.currentItem = currentItem;
|
||||
this.partialTicks= partialTicks;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
package net.minecraftforge.client.event;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import net.minecraft.client.gui.ScaledResolution;
|
||||
import net.minecraftforge.event.Cancelable;
|
||||
import net.minecraftforge.event.Event;
|
||||
|
||||
@Cancelable
|
||||
public class RenderGameOverlayEvent extends Event
|
||||
{
|
||||
public static enum ElementType
|
||||
{
|
||||
ALL,
|
||||
HELMET,
|
||||
PORTAL,
|
||||
CROSSHAIRS,
|
||||
BOSSHEALTH,
|
||||
ARMOR,
|
||||
HEALTH,
|
||||
FOOD,
|
||||
AIR,
|
||||
HOTBAR,
|
||||
EXPERIENCE,
|
||||
TEXT,
|
||||
HEALTHMOUNT,
|
||||
JUMPBAR
|
||||
}
|
||||
|
||||
public final float partialTicks;
|
||||
public final ScaledResolution resolution;
|
||||
public final int mouseX;
|
||||
public final int mouseY;
|
||||
public final ElementType type;
|
||||
|
||||
public RenderGameOverlayEvent(float partialTicks, ScaledResolution resolution, int mouseX, int mouseY)
|
||||
{
|
||||
this.partialTicks = partialTicks;
|
||||
this.resolution = resolution;
|
||||
this.mouseX = mouseX;
|
||||
this.mouseY = mouseY;
|
||||
this.type = null;
|
||||
}
|
||||
|
||||
private RenderGameOverlayEvent(RenderGameOverlayEvent parent, ElementType type)
|
||||
{
|
||||
this.partialTicks = parent.partialTicks;
|
||||
this.resolution = parent.resolution;
|
||||
this.mouseX = parent.mouseX;
|
||||
this.mouseY = parent.mouseY;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public static class Pre extends RenderGameOverlayEvent
|
||||
{
|
||||
public Pre(RenderGameOverlayEvent parent, ElementType type)
|
||||
{
|
||||
super(parent, type);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Post extends RenderGameOverlayEvent
|
||||
{
|
||||
public Post(RenderGameOverlayEvent parent, ElementType type)
|
||||
{
|
||||
super(parent, type);
|
||||
}
|
||||
@Override public boolean isCancelable(){ return false; }
|
||||
}
|
||||
|
||||
public static class Text extends Pre
|
||||
{
|
||||
public final ArrayList<String> left;
|
||||
public final ArrayList<String> right;
|
||||
public Text(RenderGameOverlayEvent parent, ArrayList<String> left, ArrayList<String> right)
|
||||
{
|
||||
super(parent, ElementType.TEXT);
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package net.minecraftforge.client.event;
|
||||
|
||||
import net.minecraft.client.renderer.entity.RendererLivingEntity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraftforge.event.Cancelable;
|
||||
import net.minecraftforge.event.Event;
|
||||
|
||||
public abstract class RenderLivingEvent extends Event
|
||||
{
|
||||
public final EntityLivingBase entity;
|
||||
public final RendererLivingEntity renderer;
|
||||
|
||||
public RenderLivingEvent(EntityLivingBase entity, RendererLivingEntity renderer)
|
||||
{
|
||||
this.entity = entity;
|
||||
this.renderer = renderer;
|
||||
}
|
||||
|
||||
public abstract static class Specials extends RenderLivingEvent
|
||||
{
|
||||
public Specials(EntityLivingBase entity, RendererLivingEntity renderer){ super(entity, renderer); }
|
||||
|
||||
@Cancelable
|
||||
public static class Pre extends Specials
|
||||
{
|
||||
public Pre(EntityLivingBase entity, RendererLivingEntity renderer){ super(entity, renderer); }
|
||||
}
|
||||
public static class Post extends Specials
|
||||
{
|
||||
public Post(EntityLivingBase entity, RendererLivingEntity renderer){ super(entity, renderer); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
package net.minecraftforge.client.event;
|
||||
|
||||
import net.minecraft.client.renderer.entity.RenderPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.event.Cancelable;
|
||||
import net.minecraftforge.event.entity.player.PlayerEvent;
|
||||
|
||||
public abstract class RenderPlayerEvent extends PlayerEvent
|
||||
{
|
||||
public final RenderPlayer renderer;
|
||||
|
||||
public RenderPlayerEvent(EntityPlayer player, RenderPlayer renderer)
|
||||
{
|
||||
super(player);
|
||||
this.renderer = renderer;
|
||||
}
|
||||
|
||||
@Cancelable
|
||||
public static class Pre extends RenderPlayerEvent
|
||||
{
|
||||
public Pre(EntityPlayer player, RenderPlayer renderer){ super(player, renderer); }
|
||||
}
|
||||
|
||||
public static class Post extends RenderPlayerEvent
|
||||
{
|
||||
public Post(EntityPlayer player, RenderPlayer renderer){ super(player, renderer); }
|
||||
}
|
||||
|
||||
public abstract static class Specials extends RenderPlayerEvent
|
||||
{
|
||||
public final float partialTicks;
|
||||
public Specials(EntityPlayer player, RenderPlayer renderer, float partialTicks)
|
||||
{
|
||||
super(player, renderer);
|
||||
this.partialTicks = partialTicks;
|
||||
}
|
||||
|
||||
@Cancelable
|
||||
public static class Pre extends Specials
|
||||
{
|
||||
public boolean renderHelmet = true;
|
||||
public boolean renderCape = true;
|
||||
public boolean renderItem = true;
|
||||
public Pre(EntityPlayer player, RenderPlayer renderer, float partialTicks){ super(player, renderer, partialTicks); }
|
||||
}
|
||||
|
||||
public static class Post extends Specials
|
||||
{
|
||||
public Post(EntityPlayer player, RenderPlayer renderer, float partialTicks){ super(player, renderer, partialTicks); }
|
||||
}
|
||||
}
|
||||
|
||||
public static class SetArmorModel extends RenderPlayerEvent
|
||||
{
|
||||
/**
|
||||
* Setting this to any value besides -1 will result in the function being
|
||||
* Immediately exited with the return value specified.
|
||||
*/
|
||||
public int result = -1;
|
||||
public final int slot;
|
||||
public final float partialTick;
|
||||
public final ItemStack stack;
|
||||
public SetArmorModel(EntityPlayer player, RenderPlayer renderer, int slot, float partialTick, ItemStack stack)
|
||||
{
|
||||
super(player, renderer);
|
||||
this.slot = slot;
|
||||
this.partialTick = partialTick;
|
||||
this.stack = stack;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package net.minecraftforge.client.event;
|
||||
|
||||
import net.minecraft.client.renderer.RenderGlobal;
|
||||
import net.minecraftforge.event.Event;
|
||||
|
||||
public class RenderWorldLastEvent extends Event
|
||||
{
|
||||
public final RenderGlobal context;
|
||||
public final float partialTicks;
|
||||
public RenderWorldLastEvent(RenderGlobal context, float partialTicks)
|
||||
{
|
||||
this.context = context;
|
||||
this.partialTicks = partialTicks;
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package net.minecraftforge.client.event;
|
||||
|
||||
import net.minecraft.client.renderer.texture.TextureMap;
|
||||
import net.minecraftforge.event.Event;
|
||||
|
||||
|
||||
public class TextureStitchEvent extends Event
|
||||
{
|
||||
public final TextureMap map;
|
||||
|
||||
public TextureStitchEvent(TextureMap map)
|
||||
{
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired when the TextureMap is told to refresh it's stitched texture.
|
||||
* Called after the Stitched list is cleared, but before any blocks or items
|
||||
* add themselves to the list.
|
||||
*/
|
||||
public static class Pre extends TextureStitchEvent
|
||||
{
|
||||
public Pre(TextureMap map){ super(map); }
|
||||
}
|
||||
|
||||
/**
|
||||
* This event is fired once the texture map has loaded all textures and
|
||||
* stitched them together. All Icons should have there locations defined
|
||||
* by the time this is fired.
|
||||
*/
|
||||
public static class Post extends TextureStitchEvent
|
||||
{
|
||||
public Post(TextureMap map){ super(map); }
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package net.minecraftforge.client.event.sound;
|
||||
|
||||
import net.minecraft.client.audio.SoundManager;
|
||||
import net.minecraft.client.audio.SoundPoolEntry;
|
||||
|
||||
/**
|
||||
* Raised when the SoundManager tries to play a Background Music file,
|
||||
* If you return null from this function it will prevent the sound from being played,
|
||||
* you can return a different entry if you want to change the sound being played.
|
||||
*/
|
||||
public class PlayBackgroundMusicEvent extends SoundResultEvent
|
||||
{
|
||||
public PlayBackgroundMusicEvent(SoundManager manager, SoundPoolEntry entry)
|
||||
{ super(manager, entry, null, 0.0f, 0.0f); }
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package net.minecraftforge.client.event.sound;
|
||||
|
||||
import net.minecraft.client.audio.SoundManager;
|
||||
import net.minecraft.client.audio.SoundPoolEntry;
|
||||
|
||||
public class PlaySoundEffectEvent extends SoundResultEvent
|
||||
{
|
||||
public PlaySoundEffectEvent(SoundManager manager, SoundPoolEntry source, String name, float volume, float pitch)
|
||||
{ super(manager, source, name, volume, pitch); }
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package net.minecraftforge.client.event.sound;
|
||||
|
||||
import net.minecraft.client.audio.SoundManager;
|
||||
|
||||
public class PlaySoundEffectSourceEvent extends SoundEvent
|
||||
{
|
||||
public final SoundManager manager;
|
||||
public final String name;
|
||||
public PlaySoundEffectSourceEvent(SoundManager manager, String name)
|
||||
{
|
||||
this.manager = manager;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package net.minecraftforge.client.event.sound;
|
||||
|
||||
import net.minecraft.client.audio.SoundManager;
|
||||
import net.minecraft.client.audio.SoundPoolEntry;
|
||||
|
||||
/***
|
||||
* Raised when the SoundManager tries to play a normal sound,
|
||||
* dogs barking, footsteps, etc. THe majority of all sounds during normal game play.
|
||||
*
|
||||
* If you return null from this function it will prevent the sound from being played,
|
||||
* you can return a different entry if you want to change the sound being played.
|
||||
*/
|
||||
public class PlaySoundEvent extends SoundResultEvent
|
||||
{
|
||||
public final float x;
|
||||
public final float y;
|
||||
public final float z;
|
||||
public PlaySoundEvent(SoundManager manager, SoundPoolEntry source, String name, float x, float y, float z, float volume, float pitch)
|
||||
{
|
||||
super(manager, source, name, volume, pitch);
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package net.minecraftforge.client.event.sound;
|
||||
|
||||
import net.minecraft.client.audio.SoundManager;
|
||||
|
||||
public class PlaySoundSourceEvent extends SoundEvent
|
||||
{
|
||||
public final SoundManager manager;
|
||||
public final String name;
|
||||
public final float x;
|
||||
public final float y;
|
||||
public final float z;
|
||||
public PlaySoundSourceEvent(SoundManager manager, String name, float x, float y, float z)
|
||||
{
|
||||
this.manager = manager;
|
||||
this.name = name;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package net.minecraftforge.client.event.sound;
|
||||
|
||||
import net.minecraft.client.audio.SoundManager;
|
||||
import net.minecraft.client.audio.SoundPoolEntry;
|
||||
|
||||
/**
|
||||
* Raised when the SoundManager tries to play a 'Streaming' file,
|
||||
* in vanilla it is only the Jukebox that uses this function.
|
||||
*
|
||||
* If you return null from this function it will prevent the sound from being played,
|
||||
* you can return a different entry if you want to change the sound being played.
|
||||
*
|
||||
*/
|
||||
public class PlayStreamingEvent extends SoundResultEvent
|
||||
{
|
||||
public final float x;
|
||||
public final float y;
|
||||
public final float z;
|
||||
public PlayStreamingEvent(SoundManager manager, SoundPoolEntry source, String name, float x, float y, float z)
|
||||
{
|
||||
super(manager, source, name, 0.0f, 0.0f);
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package net.minecraftforge.client.event.sound;
|
||||
|
||||
import net.minecraft.client.audio.SoundManager;
|
||||
|
||||
public class PlayStreamingSourceEvent extends SoundEvent
|
||||
{
|
||||
public final SoundManager manager;
|
||||
public final String name;
|
||||
public final float x;
|
||||
public final float y;
|
||||
public final float z;
|
||||
public PlayStreamingSourceEvent(SoundManager manager, String name, float x, float y, float z)
|
||||
{
|
||||
this.manager = manager;
|
||||
this.name = name;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package net.minecraftforge.client.event.sound;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.client.audio.SoundManager;
|
||||
import net.minecraft.client.audio.SoundPoolEntry;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.Cancelable;
|
||||
import net.minecraftforge.event.Event;
|
||||
|
||||
public class SoundEvent extends Event
|
||||
{
|
||||
public SoundEvent(){}
|
||||
|
||||
public static SoundPoolEntry getResult(SoundResultEvent event)
|
||||
{
|
||||
MinecraftForge.EVENT_BUS.post(event);
|
||||
return event.result;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package net.minecraftforge.client.event.sound;
|
||||
|
||||
import net.minecraft.client.audio.SoundManager;
|
||||
|
||||
/**
|
||||
* Raised by the SoundManager.loadSoundSettings, this would be a good place for
|
||||
* adding your custom sounds to the SoundPool.
|
||||
*/
|
||||
public class SoundLoadEvent extends SoundEvent
|
||||
{
|
||||
public final SoundManager manager;
|
||||
public SoundLoadEvent(SoundManager manager)
|
||||
{
|
||||
this.manager = manager;
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package net.minecraftforge.client.event.sound;
|
||||
|
||||
import net.minecraft.client.audio.SoundManager;
|
||||
import net.minecraft.client.audio.SoundPoolEntry;
|
||||
|
||||
public abstract class SoundResultEvent extends SoundEvent
|
||||
{
|
||||
public final SoundManager manager;
|
||||
public final SoundPoolEntry source;
|
||||
public final String name;
|
||||
public final float volume;
|
||||
public final float pitch;
|
||||
public SoundPoolEntry result;
|
||||
|
||||
public SoundResultEvent(SoundManager manager, SoundPoolEntry source, String name, float volume, float pitch)
|
||||
{
|
||||
this.manager = manager;
|
||||
this.source = source;
|
||||
this.name = name;
|
||||
this.volume = volume;
|
||||
this.pitch = pitch;
|
||||
this.result = source;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package net.minecraftforge.client.event.sound;
|
||||
|
||||
import net.minecraft.client.audio.SoundManager;
|
||||
|
||||
/**
|
||||
* This event is raised by the SoundManager when it does its first setup of the
|
||||
* SoundSystemConfig's codecs, use this function to add your own codecs.
|
||||
*/
|
||||
public class SoundSetupEvent extends SoundEvent
|
||||
{
|
||||
public final SoundManager manager;
|
||||
public SoundSetupEvent(SoundManager manager)
|
||||
{
|
||||
this.manager = manager;
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
package net.minecraftforge.client.model;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import net.minecraftforge.client.model.obj.ObjModelLoader;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import cpw.mods.fml.common.FMLLog;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
/**
|
||||
* Common interface for advanced model loading from files, based on file suffix
|
||||
* Model support can be queried through the {@link #getSupportedSuffixes()} method.
|
||||
* Instances can be created by calling {@link #loadModel(String)} with a class-loadable-path
|
||||
*
|
||||
* @author cpw
|
||||
*
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class AdvancedModelLoader {
|
||||
private static Map<String, IModelCustomLoader> instances = Maps.newHashMap();
|
||||
|
||||
/**
|
||||
* Register a new model handler
|
||||
* @param modelHandler The model handler to register
|
||||
*/
|
||||
public static void registerModelHandler(IModelCustomLoader modelHandler)
|
||||
{
|
||||
for (String suffix : modelHandler.getSuffixes())
|
||||
{
|
||||
instances.put(suffix, modelHandler);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the model from the supplied classpath resolvable resource name
|
||||
* @param resourceName The resource name
|
||||
* @return A model
|
||||
* @throws IllegalArgumentException if the resource name cannot be understood
|
||||
* @throws ModelFormatException if the underlying model handler cannot parse the model format
|
||||
*/
|
||||
public static IModelCustom loadModel(String resourceName) throws IllegalArgumentException, ModelFormatException
|
||||
{
|
||||
int i = resourceName.lastIndexOf('.');
|
||||
if (i == -1)
|
||||
{
|
||||
FMLLog.severe("The resource name %s is not valid", resourceName);
|
||||
throw new IllegalArgumentException("The resource name is not valid");
|
||||
}
|
||||
String suffix = resourceName.substring(i+1);
|
||||
IModelCustomLoader loader = instances.get(suffix);
|
||||
if (loader == null)
|
||||
{
|
||||
FMLLog.severe("The resource name %s is not supported", resourceName);
|
||||
throw new IllegalArgumentException("The resource name is not supported");
|
||||
}
|
||||
|
||||
URL resource = AdvancedModelLoader.class.getResource(resourceName);
|
||||
if (resource == null)
|
||||
{
|
||||
FMLLog.severe("The resource name %s could not be found", resourceName);
|
||||
throw new IllegalArgumentException("The resource name could not be found");
|
||||
}
|
||||
return loader.loadInstance(resourceName, resource);
|
||||
}
|
||||
|
||||
public static Collection<String> getSupportedSuffixes()
|
||||
{
|
||||
return instances.keySet();
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
{
|
||||
registerModelHandler(new ObjModelLoader());
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package net.minecraftforge.client.model;
|
||||
|
||||
|
||||
|
||||
public interface IModelCustom {
|
||||
String getType();
|
||||
void renderAll();
|
||||
void renderPart(String partName);
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package net.minecraftforge.client.model;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* Instances of this class act as factories for their model type
|
||||
*
|
||||
* @author cpw
|
||||
*
|
||||
*/
|
||||
public interface IModelCustomLoader {
|
||||
/**
|
||||
* Get the main type name for this loader
|
||||
* @return the type name
|
||||
*/
|
||||
String getType();
|
||||
/**
|
||||
* Get resource suffixes this model loader recognizes
|
||||
* @return a list of suffixes
|
||||
*/
|
||||
String[] getSuffixes();
|
||||
/**
|
||||
* Load a model instance from the supplied path
|
||||
* @param resourceName The resource name to load
|
||||
* @param resource The URL associated with the classloader resource
|
||||
* @return A model instance
|
||||
* @throws ModelFormatException if the model format is not correct
|
||||
*/
|
||||
IModelCustom loadInstance(String resourceName, URL resource) throws ModelFormatException;
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package net.minecraftforge.client.model;
|
||||
|
||||
/**
|
||||
* Thrown if there is a problem parsing the model
|
||||
*
|
||||
* @author cpw
|
||||
*
|
||||
*/
|
||||
public class ModelFormatException extends RuntimeException {
|
||||
|
||||
public ModelFormatException()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
public ModelFormatException(String message, Throwable cause)
|
||||
{
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public ModelFormatException(String message)
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ModelFormatException(Throwable cause)
|
||||
{
|
||||
super(cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
package net.minecraftforge.client.model.obj;
|
||||
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.util.Vec3;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class Face
|
||||
{
|
||||
|
||||
public Vertex[] vertices;
|
||||
public Vertex[] vertexNormals;
|
||||
public Vertex faceNormal;
|
||||
public TextureCoordinate[] textureCoordinates;
|
||||
|
||||
public void addFaceForRender(Tessellator tessellator)
|
||||
{
|
||||
addFaceForRender(tessellator, 0.0005F);
|
||||
}
|
||||
|
||||
public void addFaceForRender(Tessellator tessellator, float textureOffset)
|
||||
{
|
||||
if (faceNormal == null)
|
||||
{
|
||||
faceNormal = this.calculateFaceNormal();
|
||||
}
|
||||
|
||||
tessellator.setNormal(faceNormal.x, faceNormal.y, faceNormal.z);
|
||||
|
||||
float averageU = 0F;
|
||||
float averageV = 0F;
|
||||
|
||||
if ((textureCoordinates != null) && (textureCoordinates.length > 0))
|
||||
{
|
||||
for (int i = 0; i < textureCoordinates.length; ++i)
|
||||
{
|
||||
averageU += textureCoordinates[i].u;
|
||||
averageV += textureCoordinates[i].v;
|
||||
}
|
||||
|
||||
averageU = averageU / textureCoordinates.length;
|
||||
averageV = averageV / textureCoordinates.length;
|
||||
}
|
||||
|
||||
float offsetU, offsetV;
|
||||
|
||||
for (int i = 0; i < vertices.length; ++i)
|
||||
{
|
||||
|
||||
if ((textureCoordinates != null) && (textureCoordinates.length > 0))
|
||||
{
|
||||
offsetU = textureOffset;
|
||||
offsetV = textureOffset;
|
||||
|
||||
if (textureCoordinates[i].u > averageU)
|
||||
{
|
||||
offsetU = -offsetU;
|
||||
}
|
||||
if (textureCoordinates[i].v > averageV)
|
||||
{
|
||||
offsetV = -offsetV;
|
||||
}
|
||||
|
||||
tessellator.addVertexWithUV(vertices[i].x, vertices[i].y, vertices[i].z, textureCoordinates[i].u + offsetU, textureCoordinates[i].v + offsetV);
|
||||
}
|
||||
else
|
||||
{
|
||||
tessellator.addVertex(vertices[i].x, vertices[i].y, vertices[i].z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Vertex calculateFaceNormal()
|
||||
{
|
||||
Vec3 v1 = Vec3.createVectorHelper(vertices[1].x - vertices[0].x, vertices[1].y - vertices[0].y, vertices[1].z - vertices[0].z);
|
||||
Vec3 v2 = Vec3.createVectorHelper(vertices[2].x - vertices[0].x, vertices[2].y - vertices[0].y, vertices[2].z - vertices[0].z);
|
||||
Vec3 normalVector = null;
|
||||
|
||||
normalVector = v1.crossProduct(v2).normalize();
|
||||
|
||||
return new Vertex((float) normalVector.xCoord, (float) normalVector.yCoord, (float) normalVector.zCoord);
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package net.minecraftforge.client.model.obj;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class GroupObject
|
||||
{
|
||||
|
||||
public String name;
|
||||
public ArrayList<Face> faces = new ArrayList<Face>();
|
||||
public int glDrawingMode;
|
||||
|
||||
public GroupObject()
|
||||
{
|
||||
this("");
|
||||
}
|
||||
|
||||
public GroupObject(String name)
|
||||
{
|
||||
this(name, -1);
|
||||
}
|
||||
|
||||
public GroupObject(String name, int glDrawingMode)
|
||||
{
|
||||
this.name = name;
|
||||
this.glDrawingMode = glDrawingMode;
|
||||
}
|
||||
|
||||
public void render()
|
||||
{
|
||||
if (faces.size() > 0)
|
||||
{
|
||||
Tessellator tessellator = Tessellator.instance;
|
||||
tessellator.startDrawing(glDrawingMode);
|
||||
render(tessellator);
|
||||
tessellator.draw();
|
||||
}
|
||||
}
|
||||
|
||||
public void render(Tessellator tessellator)
|
||||
{
|
||||
if (faces.size() > 0)
|
||||
{
|
||||
for (Face face : faces)
|
||||
{
|
||||
face.addFaceForRender(tessellator);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package net.minecraftforge.client.model.obj;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
|
||||
import net.minecraftforge.client.model.IModelCustom;
|
||||
import net.minecraftforge.client.model.IModelCustomLoader;
|
||||
import net.minecraftforge.client.model.ModelFormatException;
|
||||
|
||||
public class ObjModelLoader implements IModelCustomLoader {
|
||||
|
||||
@Override
|
||||
public String getType()
|
||||
{
|
||||
return "OBJ model";
|
||||
}
|
||||
|
||||
private static final String[] types = { "obj" };
|
||||
@Override
|
||||
public String[] getSuffixes()
|
||||
{
|
||||
return types;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IModelCustom loadInstance(String resourceName, URL resource) throws ModelFormatException
|
||||
{
|
||||
return new WavefrontObject(resourceName, resource);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package net.minecraftforge.client.model.obj;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class TextureCoordinate
|
||||
{
|
||||
|
||||
public float u, v, w;
|
||||
|
||||
public TextureCoordinate(float u, float v)
|
||||
{
|
||||
this(u, v, 0F);
|
||||
}
|
||||
|
||||
public TextureCoordinate(float u, float v, float w)
|
||||
{
|
||||
this.u = u;
|
||||
this.v = v;
|
||||
this.w = w;
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package net.minecraftforge.client.model.obj;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class Vertex
|
||||
{
|
||||
|
||||
public float x, y, z;
|
||||
|
||||
public Vertex(float x, float y)
|
||||
{
|
||||
this(x, y, 0F);
|
||||
}
|
||||
|
||||
public Vertex(float x, float y, float z)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
}
|
||||
@@ -1,583 +0,0 @@
|
||||
package net.minecraftforge.client.model.obj;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraftforge.client.model.IModelCustom;
|
||||
import net.minecraftforge.client.model.ModelFormatException;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
/**
|
||||
* Wavefront Object importer
|
||||
* Based heavily off of the specifications found at http://en.wikipedia.org/wiki/Wavefront_.obj_file
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class WavefrontObject implements IModelCustom
|
||||
{
|
||||
|
||||
private static Pattern vertexPattern = Pattern.compile("(v( (\\-){0,1}\\d+\\.\\d+){3,4} *\\n)|(v( (\\-){0,1}\\d+\\.\\d+){3,4} *$)");
|
||||
private static Pattern vertexNormalPattern = Pattern.compile("(vn( (\\-){0,1}\\d+\\.\\d+){3,4} *\\n)|(vn( (\\-){0,1}\\d+\\.\\d+){3,4} *$)");
|
||||
private static Pattern textureCoordinatePattern = Pattern.compile("(vt( (\\-){0,1}\\d+\\.\\d+){2,3} *\\n)|(vt( (\\-){0,1}\\d+\\.\\d+){2,3} *$)");
|
||||
private static Pattern face_V_VT_VN_Pattern = Pattern.compile("(f( \\d+/\\d+/\\d+){3,4} *\\n)|(f( \\d+/\\d+/\\d+){3,4} *$)");
|
||||
private static Pattern face_V_VT_Pattern = Pattern.compile("(f( \\d+/\\d+){3,4} *\\n)|(f( \\d+/\\d+){3,4} *$)");
|
||||
private static Pattern face_V_VN_Pattern = Pattern.compile("(f( \\d+//\\d+){3,4} *\\n)|(f( \\d+//\\d+){3,4} *$)");
|
||||
private static Pattern face_V_Pattern = Pattern.compile("(f( \\d+){3,4} *\\n)|(f( \\d+){3,4} *$)");
|
||||
private static Pattern groupObjectPattern = Pattern.compile("([go]( [\\w\\d]+) *\\n)|([go]( [\\w\\d]+) *$)");
|
||||
|
||||
private static Matcher vertexMatcher, vertexNormalMatcher, textureCoordinateMatcher;
|
||||
private static Matcher face_V_VT_VN_Matcher, face_V_VT_Matcher, face_V_VN_Matcher, face_V_Matcher;
|
||||
private static Matcher groupObjectMatcher;
|
||||
|
||||
public ArrayList<Vertex> vertices = new ArrayList<Vertex>();
|
||||
public ArrayList<Vertex> vertexNormals = new ArrayList<Vertex>();
|
||||
public ArrayList<TextureCoordinate> textureCoordinates = new ArrayList<TextureCoordinate>();
|
||||
public ArrayList<GroupObject> groupObjects = new ArrayList<GroupObject>();
|
||||
private GroupObject currentGroupObject;
|
||||
private String fileName;
|
||||
|
||||
public WavefrontObject(String fileName, URL resource) throws ModelFormatException
|
||||
{
|
||||
this.fileName = fileName;
|
||||
loadObjModel(resource);
|
||||
}
|
||||
|
||||
private void loadObjModel(URL fileURL) throws ModelFormatException
|
||||
{
|
||||
BufferedReader reader = null;
|
||||
InputStream inputStream = null;
|
||||
|
||||
String currentLine = null;
|
||||
int lineCount = 0;
|
||||
|
||||
try
|
||||
{
|
||||
inputStream = fileURL.openStream();
|
||||
reader = new BufferedReader(new InputStreamReader(inputStream));
|
||||
|
||||
while ((currentLine = reader.readLine()) != null)
|
||||
{
|
||||
lineCount++;
|
||||
currentLine = currentLine.replaceAll("\\s+", " ").trim();
|
||||
|
||||
if (currentLine.startsWith("#") || currentLine.length() == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (currentLine.startsWith("v "))
|
||||
{
|
||||
Vertex vertex = parseVertex(currentLine, lineCount);
|
||||
if (vertex != null)
|
||||
{
|
||||
vertices.add(vertex);
|
||||
}
|
||||
}
|
||||
else if (currentLine.startsWith("vn "))
|
||||
{
|
||||
Vertex vertex = parseVertexNormal(currentLine, lineCount);
|
||||
if (vertex != null)
|
||||
{
|
||||
vertexNormals.add(vertex);
|
||||
}
|
||||
}
|
||||
else if (currentLine.startsWith("vt "))
|
||||
{
|
||||
TextureCoordinate textureCoordinate = parseTextureCoordinate(currentLine, lineCount);
|
||||
if (textureCoordinate != null)
|
||||
{
|
||||
textureCoordinates.add(textureCoordinate);
|
||||
}
|
||||
}
|
||||
else if (currentLine.startsWith("f "))
|
||||
{
|
||||
|
||||
if (currentGroupObject == null)
|
||||
{
|
||||
currentGroupObject = new GroupObject("Default");
|
||||
}
|
||||
|
||||
Face face = parseFace(currentLine, lineCount);
|
||||
|
||||
if (face != null)
|
||||
{
|
||||
currentGroupObject.faces.add(face);
|
||||
}
|
||||
}
|
||||
else if (currentLine.startsWith("g ") | currentLine.startsWith("o "))
|
||||
{
|
||||
GroupObject group = parseGroupObject(currentLine, lineCount);
|
||||
|
||||
if (group != null)
|
||||
{
|
||||
if (currentGroupObject != null)
|
||||
{
|
||||
groupObjects.add(currentGroupObject);
|
||||
}
|
||||
}
|
||||
|
||||
currentGroupObject = group;
|
||||
}
|
||||
}
|
||||
|
||||
groupObjects.add(currentGroupObject);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new ModelFormatException("IO Exception reading model format", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
reader.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
// hush
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
inputStream.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
// hush
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void renderAll()
|
||||
{
|
||||
Tessellator tessellator = Tessellator.instance;
|
||||
|
||||
if (currentGroupObject != null)
|
||||
{
|
||||
tessellator.startDrawing(currentGroupObject.glDrawingMode);
|
||||
}
|
||||
else
|
||||
{
|
||||
tessellator.startDrawing(GL11.GL_TRIANGLES);
|
||||
}
|
||||
|
||||
for (GroupObject groupObject : groupObjects)
|
||||
{
|
||||
groupObject.render(tessellator);
|
||||
}
|
||||
|
||||
tessellator.draw();
|
||||
}
|
||||
|
||||
public void renderOnly(String... groupNames)
|
||||
{
|
||||
for (GroupObject groupObject : groupObjects)
|
||||
{
|
||||
for (String groupName : groupNames)
|
||||
{
|
||||
if (groupName.equalsIgnoreCase(groupObject.name))
|
||||
{
|
||||
groupObject.render();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void renderPart(String partName)
|
||||
{
|
||||
for (GroupObject groupObject : groupObjects)
|
||||
{
|
||||
if (partName.equalsIgnoreCase(groupObject.name))
|
||||
{
|
||||
groupObject.render();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void renderAllExcept(String... excludedGroupNames)
|
||||
{
|
||||
for (GroupObject groupObject : groupObjects)
|
||||
{
|
||||
for (String excludedGroupName : excludedGroupNames)
|
||||
{
|
||||
if (!excludedGroupName.equalsIgnoreCase(groupObject.name))
|
||||
{
|
||||
groupObject.render();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Vertex parseVertex(String line, int lineCount) throws ModelFormatException
|
||||
{
|
||||
Vertex vertex = null;
|
||||
|
||||
if (isValidVertexLine(line))
|
||||
{
|
||||
line = line.substring(line.indexOf(" ") + 1);
|
||||
String[] tokens = line.split(" ");
|
||||
|
||||
try
|
||||
{
|
||||
if (tokens.length == 2)
|
||||
{
|
||||
return new Vertex(Float.parseFloat(tokens[0]), Float.parseFloat(tokens[1]));
|
||||
}
|
||||
else if (tokens.length == 3)
|
||||
{
|
||||
return new Vertex(Float.parseFloat(tokens[0]), Float.parseFloat(tokens[1]), Float.parseFloat(tokens[2]));
|
||||
}
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
throw new ModelFormatException(String.format("Number formatting error at line %d",lineCount), e);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ModelFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Incorrect format");
|
||||
}
|
||||
|
||||
return vertex;
|
||||
}
|
||||
|
||||
private Vertex parseVertexNormal(String line, int lineCount) throws ModelFormatException
|
||||
{
|
||||
Vertex vertexNormal = null;
|
||||
|
||||
if (isValidVertexNormalLine(line))
|
||||
{
|
||||
line = line.substring(line.indexOf(" ") + 1);
|
||||
String[] tokens = line.split(" ");
|
||||
|
||||
try
|
||||
{
|
||||
if (tokens.length == 3)
|
||||
return new Vertex(Float.parseFloat(tokens[0]), Float.parseFloat(tokens[1]), Float.parseFloat(tokens[2]));
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
throw new ModelFormatException(String.format("Number formatting error at line %d",lineCount), e);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ModelFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Incorrect format");
|
||||
}
|
||||
|
||||
return vertexNormal;
|
||||
}
|
||||
|
||||
private TextureCoordinate parseTextureCoordinate(String line, int lineCount) throws ModelFormatException
|
||||
{
|
||||
TextureCoordinate textureCoordinate = null;
|
||||
|
||||
if (isValidTextureCoordinateLine(line))
|
||||
{
|
||||
line = line.substring(line.indexOf(" ") + 1);
|
||||
String[] tokens = line.split(" ");
|
||||
|
||||
try
|
||||
{
|
||||
if (tokens.length == 2)
|
||||
return new TextureCoordinate(Float.parseFloat(tokens[0]), 1 - Float.parseFloat(tokens[1]));
|
||||
else if (tokens.length == 3)
|
||||
return new TextureCoordinate(Float.parseFloat(tokens[0]), 1 - Float.parseFloat(tokens[1]), Float.parseFloat(tokens[2]));
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
throw new ModelFormatException(String.format("Number formatting error at line %d",lineCount), e);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ModelFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Incorrect format");
|
||||
}
|
||||
|
||||
return textureCoordinate;
|
||||
}
|
||||
|
||||
private Face parseFace(String line, int lineCount) throws ModelFormatException
|
||||
{
|
||||
Face face = null;
|
||||
|
||||
if (isValidFaceLine(line))
|
||||
{
|
||||
face = new Face();
|
||||
|
||||
String trimmedLine = line.substring(line.indexOf(" ") + 1);
|
||||
String[] tokens = trimmedLine.split(" ");
|
||||
String[] subTokens = null;
|
||||
|
||||
if (tokens.length == 3)
|
||||
{
|
||||
if (currentGroupObject.glDrawingMode == -1)
|
||||
{
|
||||
currentGroupObject.glDrawingMode = GL11.GL_TRIANGLES;
|
||||
}
|
||||
else if (currentGroupObject.glDrawingMode != GL11.GL_TRIANGLES)
|
||||
{
|
||||
throw new ModelFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Invalid number of points for face (expected 4, found " + tokens.length + ")");
|
||||
}
|
||||
}
|
||||
else if (tokens.length == 4)
|
||||
{
|
||||
if (currentGroupObject.glDrawingMode == -1)
|
||||
{
|
||||
currentGroupObject.glDrawingMode = GL11.GL_QUADS;
|
||||
}
|
||||
else if (currentGroupObject.glDrawingMode != GL11.GL_QUADS)
|
||||
{
|
||||
throw new ModelFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Invalid number of points for face (expected 3, found " + tokens.length + ")");
|
||||
}
|
||||
}
|
||||
|
||||
// f v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3 ...
|
||||
if (isValidFace_V_VT_VN_Line(line))
|
||||
{
|
||||
face.vertices = new Vertex[tokens.length];
|
||||
face.textureCoordinates = new TextureCoordinate[tokens.length];
|
||||
face.vertexNormals = new Vertex[tokens.length];
|
||||
|
||||
for (int i = 0; i < tokens.length; ++i)
|
||||
{
|
||||
subTokens = tokens[i].split("/");
|
||||
|
||||
face.vertices[i] = vertices.get(Integer.parseInt(subTokens[0]) - 1);
|
||||
face.textureCoordinates[i] = textureCoordinates.get(Integer.parseInt(subTokens[1]) - 1);
|
||||
face.vertexNormals[i] = vertexNormals.get(Integer.parseInt(subTokens[2]) - 1);
|
||||
}
|
||||
|
||||
face.faceNormal = face.calculateFaceNormal();
|
||||
}
|
||||
// f v1/vt1 v2/vt2 v3/vt3 ...
|
||||
else if (isValidFace_V_VT_Line(line))
|
||||
{
|
||||
face.vertices = new Vertex[tokens.length];
|
||||
face.textureCoordinates = new TextureCoordinate[tokens.length];
|
||||
|
||||
for (int i = 0; i < tokens.length; ++i)
|
||||
{
|
||||
subTokens = tokens[i].split("/");
|
||||
|
||||
face.vertices[i] = vertices.get(Integer.parseInt(subTokens[0]) - 1);
|
||||
face.textureCoordinates[i] = textureCoordinates.get(Integer.parseInt(subTokens[1]) - 1);
|
||||
}
|
||||
|
||||
face.faceNormal = face.calculateFaceNormal();
|
||||
}
|
||||
// f v1//vn1 v2//vn2 v3//vn3 ...
|
||||
else if (isValidFace_V_VN_Line(line))
|
||||
{
|
||||
face.vertices = new Vertex[tokens.length];
|
||||
face.vertexNormals = new Vertex[tokens.length];
|
||||
|
||||
for (int i = 0; i < tokens.length; ++i)
|
||||
{
|
||||
subTokens = tokens[i].split("//");
|
||||
|
||||
face.vertices[i] = vertices.get(Integer.parseInt(subTokens[0]) - 1);
|
||||
face.vertexNormals[i] = vertexNormals.get(Integer.parseInt(subTokens[1]) - 1);
|
||||
}
|
||||
|
||||
face.faceNormal = face.calculateFaceNormal();
|
||||
}
|
||||
// f v1 v2 v3 ...
|
||||
else if (isValidFace_V_Line(line))
|
||||
{
|
||||
face.vertices = new Vertex[tokens.length];
|
||||
|
||||
for (int i = 0; i < tokens.length; ++i)
|
||||
{
|
||||
face.vertices[i] = vertices.get(Integer.parseInt(tokens[i]) - 1);
|
||||
}
|
||||
|
||||
face.faceNormal = face.calculateFaceNormal();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ModelFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Incorrect format");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ModelFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Incorrect format");
|
||||
}
|
||||
|
||||
return face;
|
||||
}
|
||||
|
||||
private GroupObject parseGroupObject(String line, int lineCount) throws ModelFormatException
|
||||
{
|
||||
GroupObject group = null;
|
||||
|
||||
if (isValidGroupObjectLine(line))
|
||||
{
|
||||
String trimmedLine = line.substring(line.indexOf(" ") + 1);
|
||||
|
||||
if (trimmedLine.length() > 0)
|
||||
{
|
||||
group = new GroupObject(trimmedLine);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ModelFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Incorrect format");
|
||||
}
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
/***
|
||||
* Verifies that the given line from the model file is a valid vertex
|
||||
* @param line the line being validated
|
||||
* @return true if the line is a valid vertex, false otherwise
|
||||
*/
|
||||
private static boolean isValidVertexLine(String line)
|
||||
{
|
||||
if (vertexMatcher != null)
|
||||
{
|
||||
vertexMatcher.reset();
|
||||
}
|
||||
|
||||
vertexMatcher = vertexPattern.matcher(line);
|
||||
return vertexMatcher.matches();
|
||||
}
|
||||
|
||||
/***
|
||||
* Verifies that the given line from the model file is a valid vertex normal
|
||||
* @param line the line being validated
|
||||
* @return true if the line is a valid vertex normal, false otherwise
|
||||
*/
|
||||
private static boolean isValidVertexNormalLine(String line)
|
||||
{
|
||||
if (vertexNormalMatcher != null)
|
||||
{
|
||||
vertexNormalMatcher.reset();
|
||||
}
|
||||
|
||||
vertexNormalMatcher = vertexNormalPattern.matcher(line);
|
||||
return vertexNormalMatcher.matches();
|
||||
}
|
||||
|
||||
/***
|
||||
* Verifies that the given line from the model file is a valid texture coordinate
|
||||
* @param line the line being validated
|
||||
* @return true if the line is a valid texture coordinate, false otherwise
|
||||
*/
|
||||
private static boolean isValidTextureCoordinateLine(String line)
|
||||
{
|
||||
if (textureCoordinateMatcher != null)
|
||||
{
|
||||
textureCoordinateMatcher.reset();
|
||||
}
|
||||
|
||||
textureCoordinateMatcher = textureCoordinatePattern.matcher(line);
|
||||
return textureCoordinateMatcher.matches();
|
||||
}
|
||||
|
||||
/***
|
||||
* Verifies that the given line from the model file is a valid face that is described by vertices, texture coordinates, and vertex normals
|
||||
* @param line the line being validated
|
||||
* @return true if the line is a valid face that matches the format "f v1/vt1/vn1 ..." (with a minimum of 3 points in the face, and a maximum of 4), false otherwise
|
||||
*/
|
||||
private static boolean isValidFace_V_VT_VN_Line(String line)
|
||||
{
|
||||
if (face_V_VT_VN_Matcher != null)
|
||||
{
|
||||
face_V_VT_VN_Matcher.reset();
|
||||
}
|
||||
|
||||
face_V_VT_VN_Matcher = face_V_VT_VN_Pattern.matcher(line);
|
||||
return face_V_VT_VN_Matcher.matches();
|
||||
}
|
||||
|
||||
/***
|
||||
* Verifies that the given line from the model file is a valid face that is described by vertices and texture coordinates
|
||||
* @param line the line being validated
|
||||
* @return true if the line is a valid face that matches the format "f v1/vt1 ..." (with a minimum of 3 points in the face, and a maximum of 4), false otherwise
|
||||
*/
|
||||
private static boolean isValidFace_V_VT_Line(String line)
|
||||
{
|
||||
if (face_V_VT_Matcher != null)
|
||||
{
|
||||
face_V_VT_Matcher.reset();
|
||||
}
|
||||
|
||||
face_V_VT_Matcher = face_V_VT_Pattern.matcher(line);
|
||||
return face_V_VT_Matcher.matches();
|
||||
}
|
||||
|
||||
/***
|
||||
* Verifies that the given line from the model file is a valid face that is described by vertices and vertex normals
|
||||
* @param line the line being validated
|
||||
* @return true if the line is a valid face that matches the format "f v1//vn1 ..." (with a minimum of 3 points in the face, and a maximum of 4), false otherwise
|
||||
*/
|
||||
private static boolean isValidFace_V_VN_Line(String line)
|
||||
{
|
||||
if (face_V_VN_Matcher != null)
|
||||
{
|
||||
face_V_VN_Matcher.reset();
|
||||
}
|
||||
|
||||
face_V_VN_Matcher = face_V_VN_Pattern.matcher(line);
|
||||
return face_V_VN_Matcher.matches();
|
||||
}
|
||||
|
||||
/***
|
||||
* Verifies that the given line from the model file is a valid face that is described by only vertices
|
||||
* @param line the line being validated
|
||||
* @return true if the line is a valid face that matches the format "f v1 ..." (with a minimum of 3 points in the face, and a maximum of 4), false otherwise
|
||||
*/
|
||||
private static boolean isValidFace_V_Line(String line)
|
||||
{
|
||||
if (face_V_Matcher != null)
|
||||
{
|
||||
face_V_Matcher.reset();
|
||||
}
|
||||
|
||||
face_V_Matcher = face_V_Pattern.matcher(line);
|
||||
return face_V_Matcher.matches();
|
||||
}
|
||||
|
||||
/***
|
||||
* Verifies that the given line from the model file is a valid face of any of the possible face formats
|
||||
* @param line the line being validated
|
||||
* @return true if the line is a valid face that matches any of the valid face formats, false otherwise
|
||||
*/
|
||||
private static boolean isValidFaceLine(String line)
|
||||
{
|
||||
return isValidFace_V_VT_VN_Line(line) || isValidFace_V_VT_Line(line) || isValidFace_V_VN_Line(line) || isValidFace_V_Line(line);
|
||||
}
|
||||
|
||||
/***
|
||||
* Verifies that the given line from the model file is a valid group (or object)
|
||||
* @param line the line being validated
|
||||
* @return true if the line is a valid group (or object), false otherwise
|
||||
*/
|
||||
private static boolean isValidGroupObjectLine(String line)
|
||||
{
|
||||
if (groupObjectMatcher != null)
|
||||
{
|
||||
groupObjectMatcher.reset();
|
||||
}
|
||||
|
||||
groupObjectMatcher = groupObjectPattern.matcher(line);
|
||||
return groupObjectMatcher.matches();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType()
|
||||
{
|
||||
return "obj";
|
||||
}
|
||||
}
|
||||
@@ -1,642 +0,0 @@
|
||||
package paulscode.sound.codecs;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.ShortBuffer;
|
||||
import javax.sound.sampled.AudioFormat;
|
||||
|
||||
import paulscode.sound.ICodec;
|
||||
import paulscode.sound.SoundBuffer;
|
||||
import paulscode.sound.SoundSystemConfig;
|
||||
import paulscode.sound.SoundSystemLogger;
|
||||
|
||||
import ibxm.FastTracker2;
|
||||
import ibxm.IBXM;
|
||||
import ibxm.Module;
|
||||
import ibxm.ProTracker;
|
||||
import ibxm.ScreamTracker3;
|
||||
|
||||
/**
|
||||
* The CodecIBXM class provides an ICodec interface for reading from MOD/S3M/XM
|
||||
* files via the IBXM library.
|
||||
*<b><i> SoundSystem CodecIBXM Class License:</b></i><br><b><br>
|
||||
* You are free to use this class for any purpose, commercial or otherwise.
|
||||
* You may modify this class or source code, and distribute it any way you
|
||||
* like, provided the following conditions are met:
|
||||
*<br>
|
||||
* 1) You may not falsely claim to be the author of this class or any
|
||||
* unmodified portion of it.
|
||||
*<br>
|
||||
* 2) You may not copyright this class or a modified version of it and then
|
||||
* sue me for copyright infringement.
|
||||
*<br>
|
||||
* 3) If you modify the source code, you must clearly document the changes
|
||||
* made before redistributing the modified source code, so other users know
|
||||
* it is not the original code.
|
||||
*<br>
|
||||
* 4) You are not required to give me credit for this class in any derived
|
||||
* work, but if you do, you must also mention my website:
|
||||
* http://www.paulscode.com
|
||||
*<br>
|
||||
* 5) I the author will not be responsible for any damages (physical,
|
||||
* financial, or otherwise) caused by the use if this class or any portion
|
||||
* of it.
|
||||
*<br>
|
||||
* 6) I the author do not guarantee, warrant, or make any representations,
|
||||
* either expressed or implied, regarding the use of this class or any
|
||||
* portion of it.
|
||||
* <br><br>
|
||||
* Author: Paul Lamb
|
||||
* <br>
|
||||
* http://www.paulscode.com
|
||||
*</b><br><br>
|
||||
*<b>
|
||||
* This software is based on or using the IBXM library available from
|
||||
* http://www.geocities.com/sunet2000/
|
||||
*</b><br><br>
|
||||
*<br><b>
|
||||
* IBXM is copyright (c) 2007, Martin Cameron, and is licensed under the BSD
|
||||
* License.
|
||||
*<br><br>
|
||||
* All rights reserved.
|
||||
*<br><br>
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*<br><br>
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer. Redistributions in binary
|
||||
* form must reproduce the above copyright notice, this list of conditions and
|
||||
* the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution. Neither the name of mumart nor the names of
|
||||
* its contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* <br><br>
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
* <br><br><br></b>
|
||||
*/
|
||||
public class CodecIBXM implements ICodec
|
||||
{
|
||||
/**
|
||||
* Used to return a current value from one of the synchronized
|
||||
* boolean-interface methods.
|
||||
*/
|
||||
private static final boolean GET = false;
|
||||
|
||||
/**
|
||||
* Used to set the value in one of the synchronized boolean-interface methods.
|
||||
*/
|
||||
private static final boolean SET = true;
|
||||
|
||||
/**
|
||||
* Used when a parameter for one of the synchronized boolean-interface methods
|
||||
* is not aplicable.
|
||||
*/
|
||||
private static final boolean XXX = false;
|
||||
|
||||
/**
|
||||
* True if there is no more data to read in.
|
||||
*/
|
||||
private boolean endOfStream = false;
|
||||
|
||||
/**
|
||||
* True if the stream has finished initializing.
|
||||
*/
|
||||
private boolean initialized = false;
|
||||
|
||||
/**
|
||||
* Format the converted audio will be in.
|
||||
*/
|
||||
private AudioFormat myAudioFormat = null;
|
||||
|
||||
/**
|
||||
* True if the using library requires data read by this codec to be
|
||||
* reverse-ordered before returning it from methods read() and readAll().
|
||||
*/
|
||||
private boolean reverseBytes = false;
|
||||
|
||||
/**
|
||||
* IBXM decoder.
|
||||
*/
|
||||
private IBXM ibxm;
|
||||
|
||||
/**
|
||||
* Module instance to be played.
|
||||
*/
|
||||
private Module module;
|
||||
|
||||
/**
|
||||
* Duration of the audio (in frames).
|
||||
*/
|
||||
private int songDuration;
|
||||
|
||||
/**
|
||||
* Audio read position (in frames).
|
||||
*/
|
||||
private int playPosition;
|
||||
|
||||
/**
|
||||
* Processes status messages, warnings, and error messages.
|
||||
*/
|
||||
private SoundSystemLogger logger;
|
||||
|
||||
/**
|
||||
* Constructor: Grabs a handle to the logger.
|
||||
*/
|
||||
public CodecIBXM()
|
||||
{
|
||||
logger = SoundSystemConfig.getLogger();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells this codec when it will need to reverse the byte order of
|
||||
* the data before returning it in the read() and readAll() methods. The
|
||||
* IBXM library produces audio data in a format that some external audio
|
||||
* libraries require to be reversed. Derivatives of the Library and Source
|
||||
* classes for audio libraries which require this type of data to be reversed
|
||||
* will call the reverseByteOrder() method.
|
||||
* @param b True if the calling audio library requires byte-reversal.
|
||||
*/
|
||||
public void reverseByteOrder( boolean b )
|
||||
{
|
||||
reverseBytes = b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares an audio stream to read from. If another stream is already opened,
|
||||
* it will be closed and a new audio stream opened in its place.
|
||||
* @param url URL to an audio file to stream from.
|
||||
* @return False if an error occurred or if end of stream was reached.
|
||||
*/
|
||||
public boolean initialize( URL url )
|
||||
{
|
||||
initialized( SET, false );
|
||||
cleanup();
|
||||
|
||||
if( url == null )
|
||||
{
|
||||
errorMessage( "url null in method 'initialize'" );
|
||||
cleanup();
|
||||
return false;
|
||||
}
|
||||
|
||||
InputStream is = null;
|
||||
|
||||
try
|
||||
{
|
||||
is = url.openStream();
|
||||
}
|
||||
catch( IOException ioe )
|
||||
{
|
||||
errorMessage( "Unable to open stream in method 'initialize'" );
|
||||
printStackTrace( ioe );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( ibxm == null )
|
||||
ibxm = new IBXM( 48000 );
|
||||
if( myAudioFormat == null )
|
||||
myAudioFormat = new AudioFormat( 48000, 16, 2, true, true );
|
||||
|
||||
try
|
||||
{
|
||||
setModule( loadModule( is ) );
|
||||
}
|
||||
catch( IllegalArgumentException iae )
|
||||
{
|
||||
errorMessage( "Illegal argument in method 'initialize'" );
|
||||
printStackTrace( iae );
|
||||
if( is != null )
|
||||
{
|
||||
try
|
||||
{
|
||||
is.close();
|
||||
}
|
||||
catch( IOException ioe )
|
||||
{}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch( IOException ioe )
|
||||
{
|
||||
errorMessage( "Error loading module in method 'initialize'" );
|
||||
printStackTrace( ioe );
|
||||
if( is != null )
|
||||
{
|
||||
try
|
||||
{
|
||||
is.close();
|
||||
}
|
||||
catch( IOException ioe2 )
|
||||
{}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if( is != null )
|
||||
{
|
||||
try
|
||||
{
|
||||
is.close();
|
||||
}
|
||||
catch( IOException ioe )
|
||||
{}
|
||||
}
|
||||
|
||||
endOfStream( SET, false );
|
||||
initialized( SET, true );
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns false if the stream is busy initializing.
|
||||
* @return True if steam is initialized.
|
||||
*/
|
||||
public boolean initialized()
|
||||
{
|
||||
return initialized( GET, XXX );
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads in one stream buffer worth of audio data. See
|
||||
* {@link paulscode.sound.SoundSystemConfig SoundSystemConfig} for more
|
||||
* information about accessing and changing default settings.
|
||||
* @return The audio data wrapped into a SoundBuffer context.
|
||||
*/
|
||||
public SoundBuffer read()
|
||||
{
|
||||
if( endOfStream( GET, XXX ) )
|
||||
return null;
|
||||
|
||||
if( module == null )
|
||||
{
|
||||
errorMessage( "Module null in method 'read'" );
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check to make sure there is an audio format:
|
||||
if( myAudioFormat == null )
|
||||
{
|
||||
errorMessage( "Audio Format null in method 'read'" );
|
||||
return null;
|
||||
}
|
||||
|
||||
int bufferFrameSize = (int) SoundSystemConfig.getStreamingBufferSize()
|
||||
/ 4;
|
||||
|
||||
int frames = songDuration - playPosition;
|
||||
if( frames > bufferFrameSize )
|
||||
frames = bufferFrameSize;
|
||||
|
||||
if( frames <= 0 )
|
||||
{
|
||||
endOfStream( SET, true );
|
||||
return null;
|
||||
}
|
||||
byte[] outputBuffer = new byte[ frames * 4 ];
|
||||
|
||||
ibxm.get_audio( outputBuffer, frames );
|
||||
|
||||
playPosition += frames;
|
||||
if( playPosition >= songDuration )
|
||||
{
|
||||
endOfStream( SET, true );
|
||||
}
|
||||
|
||||
// Reverse the byte order if necessary:
|
||||
if( reverseBytes )
|
||||
reverseBytes( outputBuffer, 0, frames * 4 );
|
||||
|
||||
// Wrap the data into a SoundBuffer:
|
||||
SoundBuffer buffer = new SoundBuffer( outputBuffer, myAudioFormat );
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads in all the audio data from the stream (up to the default
|
||||
* "maximum file size". See
|
||||
* {@link paulscode.sound.SoundSystemConfig SoundSystemConfig} for more
|
||||
* information about accessing and changing default settings.
|
||||
* @return the audio data wrapped into a SoundBuffer context.
|
||||
*/
|
||||
public SoundBuffer readAll()
|
||||
{
|
||||
if( module == null )
|
||||
{
|
||||
errorMessage( "Module null in method 'readAll'" );
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check to make sure there is an audio format:
|
||||
if( myAudioFormat == null )
|
||||
{
|
||||
errorMessage( "Audio Format null in method 'readAll'" );
|
||||
return null;
|
||||
}
|
||||
|
||||
int bufferFrameSize = (int) SoundSystemConfig.getFileChunkSize()
|
||||
/ 4;
|
||||
|
||||
byte[] outputBuffer = new byte[ bufferFrameSize * 4 ];
|
||||
|
||||
// Buffer to contain the audio data:
|
||||
byte[] fullBuffer = null;
|
||||
// frames of audio data:
|
||||
int frames;
|
||||
// bytes of audio data:
|
||||
int totalBytes = 0;
|
||||
|
||||
while( (!endOfStream(GET, XXX)) &&
|
||||
(totalBytes < SoundSystemConfig.getMaxFileSize()) )
|
||||
{
|
||||
frames = songDuration - playPosition;
|
||||
if( frames > bufferFrameSize )
|
||||
frames = bufferFrameSize;
|
||||
ibxm.get_audio( outputBuffer, frames );
|
||||
totalBytes += (frames * 4);
|
||||
|
||||
fullBuffer = appendByteArrays( fullBuffer, outputBuffer,
|
||||
frames * 4 );
|
||||
|
||||
playPosition += frames;
|
||||
if( playPosition >= songDuration )
|
||||
{
|
||||
endOfStream( SET, true );
|
||||
}
|
||||
}
|
||||
|
||||
// Reverse the byte order if necessary:
|
||||
if( reverseBytes )
|
||||
reverseBytes( fullBuffer, 0, totalBytes );
|
||||
|
||||
// Wrap the data into a SoundBuffer:
|
||||
SoundBuffer buffer = new SoundBuffer( fullBuffer, myAudioFormat );
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns false if there is still more data available to be read in.
|
||||
* @return True if end of stream was reached.
|
||||
*/
|
||||
public boolean endOfStream()
|
||||
{
|
||||
return endOfStream( GET, XXX );
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the audio stream and remove references to all instantiated objects.
|
||||
*/
|
||||
public void cleanup()
|
||||
{
|
||||
// if( ibxm != null )
|
||||
// ibxm.seek( 0 );
|
||||
playPosition = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the audio format of the data being returned by the read() and
|
||||
* readAll() methods.
|
||||
* @return Information wrapped into an AudioFormat context.
|
||||
*/
|
||||
public AudioFormat getAudioFormat()
|
||||
{
|
||||
return myAudioFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the data in the specified InputStream into an instance of
|
||||
* ibxm.Module.
|
||||
* @param input an InputStream containing the module file to be decoded.
|
||||
* @throws IllegalArgumentException if the data is not recognised as a module file.
|
||||
*/
|
||||
private static Module loadModule( InputStream input )
|
||||
throws IllegalArgumentException, IOException
|
||||
{
|
||||
DataInputStream data_input_stream = new DataInputStream( input );
|
||||
|
||||
// Check if data is in XM format:
|
||||
byte[] xm_header = new byte[ 60 ];
|
||||
data_input_stream.readFully( xm_header );
|
||||
if( FastTracker2.is_xm( xm_header ) )
|
||||
return FastTracker2.load_xm( xm_header, data_input_stream );
|
||||
|
||||
// Check if data is in ScreamTracker 3 format:
|
||||
byte[] s3m_header = new byte[ 96 ];
|
||||
System.arraycopy( xm_header, 0, s3m_header, 0, 60 );
|
||||
data_input_stream.readFully( s3m_header, 60, 36 );
|
||||
if( ScreamTracker3.is_s3m( s3m_header ) )
|
||||
return ScreamTracker3.load_s3m( s3m_header, data_input_stream );
|
||||
|
||||
// Check if data is in ProTracker format:
|
||||
byte[] mod_header = new byte[ 1084 ];
|
||||
System.arraycopy( s3m_header, 0, mod_header, 0, 96 );
|
||||
data_input_stream.readFully( mod_header, 96, 988 );
|
||||
return ProTracker.load_mod( mod_header, data_input_stream );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Module instance to be played.
|
||||
*/
|
||||
private void setModule( Module m )
|
||||
{
|
||||
if( m != null )
|
||||
module = m;
|
||||
ibxm.set_module( module );
|
||||
songDuration = ibxm.calculate_song_duration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method for synchronizing access to the boolean 'initialized'.
|
||||
* @param action GET or SET.
|
||||
* @param value New value if action == SET, or XXX if action == GET.
|
||||
* @return True if steam is initialized.
|
||||
*/
|
||||
private synchronized boolean initialized( boolean action, boolean value )
|
||||
{
|
||||
if( action == SET )
|
||||
initialized = value;
|
||||
return initialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method for synchronizing access to the boolean 'endOfStream'.
|
||||
* @param action GET or SET.
|
||||
* @param value New value if action == SET, or XXX if action == GET.
|
||||
* @return True if end of stream was reached.
|
||||
*/
|
||||
private synchronized boolean endOfStream( boolean action, boolean value )
|
||||
{
|
||||
if( action == SET )
|
||||
endOfStream = value;
|
||||
return endOfStream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trims down the size of the array if it is larger than the specified
|
||||
* maximum length.
|
||||
* @param array Array containing audio data.
|
||||
* @param maxLength Maximum size this array may be.
|
||||
* @return New array.
|
||||
*/
|
||||
private static byte[] trimArray( byte[] array, int maxLength )
|
||||
{
|
||||
byte[] trimmedArray = null;
|
||||
if( array != null && array.length > maxLength )
|
||||
{
|
||||
trimmedArray = new byte[maxLength];
|
||||
System.arraycopy( array, 0, trimmedArray, 0, maxLength );
|
||||
}
|
||||
return trimmedArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse-orders all bytes contained in the specified array.
|
||||
* @param buffer Array containing audio data.
|
||||
*/
|
||||
public static void reverseBytes( byte[] buffer )
|
||||
{
|
||||
reverseBytes( buffer, 0, buffer.length );
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse-orders the specified range of bytes contained in the specified array.
|
||||
* @param buffer Array containing audio data.
|
||||
* @param offset Array index to begin.
|
||||
* @param size number of bytes to reverse-order.
|
||||
*/
|
||||
public static void reverseBytes( byte[] buffer, int offset, int size )
|
||||
{
|
||||
|
||||
byte b;
|
||||
for( int i = offset; i < ( offset + size ); i += 2 )
|
||||
{
|
||||
b = buffer[i];
|
||||
buffer[i] = buffer[i + 1];
|
||||
buffer[i + 1] = b;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts sound bytes to little-endian format.
|
||||
* @param audio_bytes The original wave data
|
||||
* @param two_bytes_data For stereo sounds.
|
||||
* @return byte array containing the converted data.
|
||||
*/
|
||||
private static byte[] convertAudioBytes( byte[] audio_bytes,
|
||||
boolean two_bytes_data )
|
||||
{
|
||||
ByteBuffer dest = ByteBuffer.allocateDirect( audio_bytes.length );
|
||||
dest.order( ByteOrder.nativeOrder() );
|
||||
ByteBuffer src = ByteBuffer.wrap( audio_bytes );
|
||||
src.order( ByteOrder.LITTLE_ENDIAN );
|
||||
if( two_bytes_data )
|
||||
{
|
||||
ShortBuffer dest_short = dest.asShortBuffer();
|
||||
ShortBuffer src_short = src.asShortBuffer();
|
||||
while( src_short.hasRemaining() )
|
||||
{
|
||||
dest_short.put(src_short.get());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while( src.hasRemaining() )
|
||||
{
|
||||
dest.put( src.get() );
|
||||
}
|
||||
}
|
||||
dest.rewind();
|
||||
|
||||
if( !dest.hasArray() )
|
||||
{
|
||||
byte[] arrayBackedBuffer = new byte[dest.capacity()];
|
||||
dest.get( arrayBackedBuffer );
|
||||
dest.clear();
|
||||
|
||||
return arrayBackedBuffer;
|
||||
}
|
||||
|
||||
return dest.array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new array with the second array appended to the end of the first
|
||||
* array.
|
||||
* @param arrayOne The first array.
|
||||
* @param arrayTwo The second array.
|
||||
* @param length How many bytes to append from the second array.
|
||||
* @return Byte array containing information from both arrays.
|
||||
*/
|
||||
private static byte[] appendByteArrays( byte[] arrayOne, byte[] arrayTwo,
|
||||
int length )
|
||||
{
|
||||
byte[] newArray;
|
||||
if( arrayOne == null && arrayTwo == null )
|
||||
{
|
||||
// no data, just return
|
||||
return null;
|
||||
}
|
||||
else if( arrayOne == null )
|
||||
{
|
||||
// create the new array, same length as arrayTwo:
|
||||
newArray = new byte[ length ];
|
||||
// fill the new array with the contents of arrayTwo:
|
||||
System.arraycopy( arrayTwo, 0, newArray, 0, length );
|
||||
arrayTwo = null;
|
||||
}
|
||||
else if( arrayTwo == null )
|
||||
{
|
||||
// create the new array, same length as arrayOne:
|
||||
newArray = new byte[ arrayOne.length ];
|
||||
// fill the new array with the contents of arrayOne:
|
||||
System.arraycopy( arrayOne, 0, newArray, 0, arrayOne.length );
|
||||
arrayOne = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
// create the new array large enough to hold both arrays:
|
||||
newArray = new byte[ arrayOne.length + length ];
|
||||
System.arraycopy( arrayOne, 0, newArray, 0, arrayOne.length );
|
||||
// fill the new array with the contents of both arrays:
|
||||
System.arraycopy( arrayTwo, 0, newArray, arrayOne.length,
|
||||
length );
|
||||
arrayOne = null;
|
||||
arrayTwo = null;
|
||||
}
|
||||
|
||||
return newArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints an error message.
|
||||
* @param message Message to print.
|
||||
*/
|
||||
private void errorMessage( String message )
|
||||
{
|
||||
logger.errorMessage( "CodecWav", message, 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints an exception's error message followed by the stack trace.
|
||||
* @param e Exception containing the information to print.
|
||||
*/
|
||||
private void printStackTrace( Exception e )
|
||||
{
|
||||
logger.printStackTrace( e, 1 );
|
||||
}
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
#Main Forge Access Transformer configuration file
|
||||
# Tessellator
|
||||
public-f bff.a #FD:Tessellator/field_78398_a #instance
|
||||
public bff.u #FD:Tessellator/field_78409_u #drawMode
|
||||
public bff.v #FD:Tessellator/field_78408_v #xOffset
|
||||
public bff.w #FD:Tessellator/field_78407_w #yOffset
|
||||
public bff.x #FD:Tessellator/field_78417_x #zOffset
|
||||
public bff.z #FD:Tessellator/field_78415_z #isDrawing
|
||||
# ItemPickaxe
|
||||
public yi.<init>(ILxy;)V #MD:ItemPickaxe/<init>(ILnet/minecraft/src/EnumToolMaterial;) #constructor
|
||||
public+f yi.c #FD:ItemPickaxe/field_77867_c #blocksEffectiveAgainst
|
||||
# ItemAxe
|
||||
public xv.<init>(ILxy;)V #MD:ItemAxe/<init>(ILnet/minecraft/src/EnumToolMaterial;) #constructor
|
||||
public+f xv.c #FD:ItemAxe/field_77868_c #blocksEffectiveAgainst
|
||||
# ItemSpade
|
||||
public yt.<init>(ILxy;)V #MD:ItemSpade/<init>(ILnet/minecraft/src/EnumToolMaterial;) #constructor
|
||||
public+f yt.c #FD:ItemSpade/field_77866_c #blocksEffectiveAgainst
|
||||
# ItemTool
|
||||
public xe.a #FD:ItemTool/field_77864_a #efficiencyOnProperMaterial
|
||||
public xe.d #FD:ItemTool/field_77865_bY #damageVsEntity
|
||||
# EntityEnderman
|
||||
public td.br #FD:EntityEnderman/field_70827_d #carriableBlocks
|
||||
# RenderEngine
|
||||
# -- MISSING MAPPING public bge.f(Ljava/lang/String;)I #MD:RenderEngine/func_78341_b #getTexture
|
||||
# -- MISSING MAPPING public bge.i #FD:RenderEngine/field_94154_l #terrainTextureMap
|
||||
# -- MISSING MAPPING public bge.j #FD:RenderEngine/field_94155_m #itemTextureMap
|
||||
# RenderGlobal
|
||||
public bfa.k #FD:RenderGlobal/field_72769_h #theWorld
|
||||
public bfa.l #FD:RenderGlobal/field_72770_i #renderEngine
|
||||
public bfa.t #FD:RenderGlobal/field_72777_q #mc
|
||||
public bfa.u #FD:RenderGlobal/field_72776_r #globalRenderBlocks
|
||||
public bfa.H #FD:RenderGlobal/field_72738_E #damagedBlocks
|
||||
# SoundManager
|
||||
public blc.b #FD:SoundManager/field_77381_a #sndSystem
|
||||
public blc.d #FD:SoundManager/field_77379_b #soundPoolSounds
|
||||
public blc.e #FD:SoundManager/field_77380_c #soundPoolStreaming
|
||||
public blc.f #FD:SoundManager/field_77377_d #soundPoolMusic
|
||||
# EntityMinecart
|
||||
protected sq.* #FD:EntityMinecart/* # All private -> protected
|
||||
# -- MISSING MAPPING public py.h()Z #MD:EntityMinecart/func_70490_h #isMinecartPowered
|
||||
# Block
|
||||
public aqs.<init>(ILajv;)V #MD:Block/<init>(ILnet/minecraft/src/Material;) #Constructor
|
||||
public aqs.<init>(IILajv;)V #MD:Block/<init>(IILnet/minecraft/src/Material;) #Constructor
|
||||
public aqs.cH #FD:Block/field_72029_cc #blockResistance
|
||||
public aqs.cG #FD:Block/field_71989_cb #blockHardness
|
||||
# -- MISSING MAPPING public amq.r()Lamq; #MD:Block/func_71912_p #setRequiresSelfNotify
|
||||
public aqs.a(Laqw;)Laqs; #MD:Block/func_71884_a #setStepSound
|
||||
public aqs.b(F)Laqs; #MD:Block/func_71894_b #setResistance
|
||||
public aqs.c(F)Laqs; #MD:Block/func_71848_c #setHardness
|
||||
public aqs.k(I)Laqs; #MD:Block/func_71868_h #setLightOpacity
|
||||
public aqs.a(F)Laqs; #MD:Block/func_71900_a #setLightValue
|
||||
public aqs.r()Laqs; #MD:Block/func_71875_q #setBlockUnbreakable
|
||||
public aqs.b(Z)Laqs; #MD:Block/func_71907_b #setTickRandomly
|
||||
public aqs.a(FFFFFF)V #MD:Block/func_71905_a #setBlockBounds
|
||||
# NetServerHandler
|
||||
public jx.f #FD:NetServerHandler/field_72572_g #playerInAirTime
|
||||
# TileEntity
|
||||
public asi.k #FD:TileEntity/field_70331_k #worldObj
|
||||
# BlockLeavesBase
|
||||
public ara.d #FD:BlockLeavesBase/field_72131_c #graphicsLevel
|
||||
# Item
|
||||
public xx.<init>(I)V #MD:Item/<init>(I) #Constructor
|
||||
public xx.e(I)Lxx; #MD:Item/func_77656_e #setMaxDamage
|
||||
public-f xx.h(Lxz;)Lmp; #MD:Item/func_77650_f #getIconIndex
|
||||
public xx.c(Ljava/lang/String;)Lxx; #MD:Item/func_77631_c #setPotionEffect
|
||||
# RailLogic
|
||||
public ams #CL:BlockBaseRailLogic
|
||||
public ams.a()I #MD:BlockBaseRailLogic/func_94505_a #getNAdjacentTiles
|
||||
# EntityPlayer
|
||||
public ua.a(Lsp;)V #MD:EntityPlayer/func_71012_a #joinEntityItemWithWorld
|
||||
public ua.i()V #MD:EntityPlayer/func_71053_j #closeScreen
|
||||
public ua.b #FD:EntityPlayer/field_71076_b #sleepTimer
|
||||
# EntityPlayerMP
|
||||
public bcx.a(Lsp;)V #MD:EntityClientPlayerMP/func_71012_a #joinEntityItemWithWorld
|
||||
# World Gen Chests Related
|
||||
public mh.* #FD:WeightedRandomChestContent/* #all
|
||||
public jp.T #FD:WorldServer/field_73069_S #bonusChestContent
|
||||
public agc.a #FD:StructureMineshaftPieces/field_78818_a #mineshaftChestContents
|
||||
public ahg.i #FD:ComponentScatteredFeatureDesertPyramid/field_74941_i #itemsToGenerateInTemple
|
||||
public ahh.l #FD:ComponentScatteredFeatureJunglePyramid/field_74943_l #junglePyramidsChestContents
|
||||
public ahh.m #FD:ComponentScatteredFeatureJunglePyramid/field_74944_m #junglePyramidsDispenserContents
|
||||
public ahr.a #FD:ComponentStrongholdChestCorridor/field_75003_a #strongholdChestContents
|
||||
public ahv.b #FD:ComponentStrongholdLibrary/field_75007_b #strongholdLibraryChestContents
|
||||
public aia.c #FD:ComponentStrongholdRoomCrossing/field_75014_c #strongholdCorridorChestContents
|
||||
public ajb.a #FD:ComponentVillageHouse2/field_74918_a #villageBlacksmithChestContents
|
||||
# AnvilChunkLoader.chunkSaveLocation
|
||||
public adz.d #FD:AnvilChunkLoader/field_75825_d
|
||||
# ChunkProviderServer.currentChunkLoader
|
||||
public jo.e #FD:ChunkProviderServer/field_73247_e
|
||||
# PlayerManager
|
||||
public jm.a(IIZ)Ljn; #MD:PlayerManager/func_72690_a #getOrCreateChunkWatcher
|
||||
# PlayerInstance
|
||||
public jn #CL:PlayerInstance
|
||||
# World
|
||||
public-f abr.A #FD:World/field_72982_D #villageCollectionObj
|
||||
public abr.G #FD:World/field_72993_I #activeChunkSet
|
||||
# EntityLiving
|
||||
public od.b #FD:EntityLiving/field_70728_aV #experienceValue
|
||||
# -- MISSING MAPPING public ng.bt #FD:EntityLiving/field_94063_bt #combatTracker
|
||||
public od.d #FD:EntityLiving/field_70715_bh #targetTasks
|
||||
# GuiFlatPresets
|
||||
public avq.a(Ljava/lang/String;ILacl;Ljava/util/List;[Lafy;)V #MD:GuiFlatPresets/func_82294_a
|
||||
public avq.a(Ljava/lang/String;ILacl;[Lafy;)V #MD:GuiFlatPresets/func_82297_a
|
||||
# BiomeGenBase
|
||||
public acl.*() #MD:BiomeGenBase/* #Everything protected->public
|
||||
# MapGenVillage
|
||||
public-f aip.e #FD:MapGenVillage/field_75055_e #villageSpawnBiomes
|
||||
# ShapedRecipes
|
||||
public+f aad.d #FD:ShapedRecipes/field_77574_d #recipeItems
|
||||
public+f aad.b #FD:ShapedRecipes/field_77576_b #recipeWidth
|
||||
public+f aad.c #FD:ShapedRecipes/field_77577_c #recipeHeight
|
||||
# ShapelessRecipes
|
||||
public aae.b #FD:ShapelessRecipes/field_77579_b #recipeItems
|
||||
# GuiContainer
|
||||
protected awp.a(Lvz;)V #MD:GuiContainer/func_74192_a #drawSlotInventory
|
||||
# ContainerPlayer
|
||||
protected vq.h #FD:ContainerPlayer/field_82862_h #player
|
||||
# BlockButton
|
||||
protected amy.n(Labr;III)V #MD:BlockButton/func_82535_o #checkActivation
|
||||
protected-f amy.a #FD:BlockButton/field_82537_a #sensible
|
||||
# BiomeDecorator
|
||||
public acp.* #FD:BiomeDecorator/* # All private -> protected
|
||||
# CreativeTabs
|
||||
public-f wr.a #FD:CreativeTabs/field_78032_a # creativeTabArray non-final
|
||||
# Packet
|
||||
public ex.a(IZZLjava/lang/Class;)V #MD:Packet/func_73285_a #addIdClassMapping
|
||||
# SaveHandler
|
||||
public alj.b()Ljava/io/File; #MD:SaveHandler/func_75765_b
|
||||
# World stuff
|
||||
public abr.b(Lnk;)V #MD:World/func_72847_b #releaseEntitySkin
|
||||
public abr.m #FD:World/field_73003_n #prevRainingStrength
|
||||
public abr.n #FD:World/field_73004_o #rainingStrength
|
||||
public abr.p #FD:World/field_73017_q #thunderingStrength
|
||||
public abr.o #FD:World/field_73018_p #prevThunderingStrength
|
||||
#WorldClient
|
||||
public bcu.b(Lnk;)V #MD:WorldClient/func_72847_b #releaseEntitySkin
|
||||
#WorldServer
|
||||
public jp.b(Lnk;)V #MD:WorldServer/func_72847_b #releaseEntitySkin
|
||||
public jp.N #FD:WorldServer/field_73068_P #allPlayersSleeping
|
||||
#TextureMap
|
||||
public bhz.g #FD:TextureMap/field_94255_a
|
||||
# -- MISSING MAPPING public bir.b #FD:TextureMap/field_94253_b
|
||||
public bhz.h #FD:TextureMap/field_94254_c
|
||||
# -- MISSING MAPPING public bir.d #FD:TextureMap/field_94251_d
|
||||
#Potion
|
||||
public nf.b(II)Lnf; #MD:Potion/func_76399_b #setIconIndex
|
||||
#PotionHelper
|
||||
public zk.m #FD:PotionHelper/field_77927_l #potionRequirements
|
||||
public zk.n #FD:PotionHelper/field_77928_m #potionAmplifiers
|
||||
#PotionEffect
|
||||
public ng.b #FD:PotionEffect/field_76460_b #duration
|
||||
#BlockFluid
|
||||
protected aov.a #FD:BlockFluid/field_94425_a #theIcon
|
||||
#GuiIngame
|
||||
protected ava.* #FD:GuiIngame/* # All private -> protected
|
||||
protected ava.*() #MD:GuiIngame/* # All private -> protected
|
||||
#ItemStack
|
||||
default xz.f #FD:ItemStack/field_77991_e # make default access for itemDamage
|
||||
#GuiSlot
|
||||
protected avx.b(IIII)V #MD:GuiSlot/func_77206_b #overlayBackground
|
||||
#EntityPlayer
|
||||
public ua.bu #FD:EntityPlayer/field_71092_bJ #username
|
||||
@@ -1,43 +0,0 @@
|
||||
package net.minecraftforge.classloading;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
|
||||
|
||||
public class FMLForgePlugin implements IFMLLoadingPlugin
|
||||
{
|
||||
public static boolean RUNTIME_DEOBF = false;
|
||||
|
||||
@Override
|
||||
public String[] getLibraryRequestClass()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getASMTransformerClass()
|
||||
{
|
||||
return new String[]{
|
||||
"net.minecraftforge.transformers.ForgeAccessTransformer",
|
||||
"net.minecraftforge.transformers.EventTransformer"
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModContainerClass()
|
||||
{
|
||||
return "net.minecraftforge.common.ForgeDummyContainer";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSetupClass()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectData(Map<String, Object> data)
|
||||
{
|
||||
RUNTIME_DEOBF = (Boolean)data.get("runtimeDeobfuscationEnabled");
|
||||
}
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
/**
|
||||
* This software is provided under the terms of the Minecraft Forge Public
|
||||
* License v1.0.
|
||||
*/
|
||||
|
||||
package net.minecraftforge.common;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import net.minecraft.stats.Achievement;
|
||||
|
||||
public class AchievementPage
|
||||
{
|
||||
private String name;
|
||||
private LinkedList<Achievement> achievements;
|
||||
|
||||
public AchievementPage(String name, Achievement... achievements)
|
||||
{
|
||||
this.name = name;
|
||||
this.achievements = new LinkedList<Achievement>(Arrays.asList(achievements));
|
||||
}
|
||||
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
public List<Achievement> getAchievements()
|
||||
{
|
||||
return achievements;
|
||||
}
|
||||
|
||||
private static LinkedList<AchievementPage> achievementPages = new LinkedList<AchievementPage>();
|
||||
|
||||
/**
|
||||
* Registers an achievement page.
|
||||
* @param page The page.
|
||||
*/
|
||||
public static void registerAchievementPage(AchievementPage page)
|
||||
{
|
||||
if (getAchievementPage(page.getName()) != null)
|
||||
{
|
||||
throw new RuntimeException("Duplicate achievement page name \"" + page.getName() + "\"!");
|
||||
}
|
||||
achievementPages.add(page);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will return an achievement page by its index on the list.
|
||||
* @param index The page's index.
|
||||
* @return the achievement page corresponding to the index or null if invalid index
|
||||
*/
|
||||
public static AchievementPage getAchievementPage(int index)
|
||||
{
|
||||
return achievementPages.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will return an achievement page by its name.
|
||||
* @param name The page's name.
|
||||
* @return the achievement page with the given name or null if no such page
|
||||
*/
|
||||
public static AchievementPage getAchievementPage(String name)
|
||||
{
|
||||
for (AchievementPage page : achievementPages)
|
||||
{
|
||||
if (page.getName().equals(name))
|
||||
{
|
||||
return page;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will return the list of achievement pages.
|
||||
* @return the list's size
|
||||
*/
|
||||
public static Set<AchievementPage> getAchievementPages()
|
||||
{
|
||||
return new HashSet<AchievementPage>(achievementPages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will return whether an achievement is in any page or not.
|
||||
* @param achievement The achievement.
|
||||
*/
|
||||
public static boolean isAchievementInPages(Achievement achievement)
|
||||
{
|
||||
for (AchievementPage page : achievementPages)
|
||||
{
|
||||
if (page.getAchievements().contains(achievement))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static String getTitle(int index)
|
||||
{
|
||||
return index == -1 ? "Minecraft" : getAchievementPage(index).getName();
|
||||
}
|
||||
}
|
||||
@@ -1,301 +0,0 @@
|
||||
package net.minecraftforge.common;
|
||||
|
||||
import java.util.*;
|
||||
import net.minecraft.world.biome.*;
|
||||
import static net.minecraft.world.biome.BiomeGenBase.*;
|
||||
import static net.minecraftforge.common.BiomeDictionary.Type.*;
|
||||
|
||||
public class BiomeDictionary
|
||||
{
|
||||
public enum Type
|
||||
{
|
||||
FOREST,
|
||||
PLAINS,
|
||||
MOUNTAIN,
|
||||
HILLS,
|
||||
SWAMP,
|
||||
WATER,
|
||||
DESERT,
|
||||
FROZEN,
|
||||
JUNGLE,
|
||||
WASTELAND,
|
||||
BEACH,
|
||||
NETHER,
|
||||
END,
|
||||
MUSHROOM,
|
||||
MAGICAL;
|
||||
}
|
||||
|
||||
private static final int BIOME_LIST_SIZE = 256;
|
||||
private static BiomeInfo[] biomeList = new BiomeInfo[BIOME_LIST_SIZE];
|
||||
private static ArrayList<BiomeGenBase>[] typeInfoList = new ArrayList[Type.values().length];
|
||||
|
||||
private static class BiomeInfo
|
||||
{
|
||||
public EnumSet<Type> typeList;
|
||||
|
||||
public BiomeInfo(Type[] types)
|
||||
{
|
||||
typeList = EnumSet.noneOf(Type.class);
|
||||
for(Type t : types)
|
||||
{
|
||||
typeList.add(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
{
|
||||
registerVanillaBiomes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a biome with a specific biome type
|
||||
*
|
||||
* @param biome the biome to be registered
|
||||
* @param type the type to register the biome as
|
||||
* @return returns true if the biome was registered successfully
|
||||
*/
|
||||
public static boolean registerBiomeType(BiomeGenBase biome, Type ... types)
|
||||
{
|
||||
if(BiomeGenBase.biomeList[biome.biomeID] != null)
|
||||
{
|
||||
for(Type type : types)
|
||||
{
|
||||
if(typeInfoList[type.ordinal()] == null)
|
||||
{
|
||||
typeInfoList[type.ordinal()] = new ArrayList<BiomeGenBase>();
|
||||
}
|
||||
|
||||
typeInfoList[type.ordinal()].add(biome);
|
||||
}
|
||||
|
||||
if(biomeList[biome.biomeID] == null)
|
||||
{
|
||||
biomeList[biome.biomeID] = new BiomeInfo(types);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(Type type : types)
|
||||
{
|
||||
biomeList[biome.biomeID].typeList.add(type);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of biomes registered with a specific type
|
||||
*
|
||||
* @param type the Type to look for
|
||||
* @return a list of biomes of the specified type, null if there are none
|
||||
*/
|
||||
public static BiomeGenBase[] getBiomesForType(Type type)
|
||||
{
|
||||
if(typeInfoList[type.ordinal()] != null)
|
||||
{
|
||||
return (BiomeGenBase[])typeInfoList[type.ordinal()].toArray(new BiomeGenBase[0]);
|
||||
}
|
||||
|
||||
return new BiomeGenBase[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of Types that a specific biome is registered with
|
||||
*
|
||||
* @param biome the biome to check
|
||||
* @return the list of types, null if there are none
|
||||
*/
|
||||
public static Type[] getTypesForBiome(BiomeGenBase biome)
|
||||
{
|
||||
checkRegistration(biome);
|
||||
|
||||
if(biomeList[biome.biomeID] != null)
|
||||
{
|
||||
return (Type[])biomeList[biome.biomeID].typeList.toArray(new Type[0]);
|
||||
}
|
||||
|
||||
return new Type[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if two biomes are registered as having the same type
|
||||
*
|
||||
* @param biomeA
|
||||
* @param biomeB
|
||||
* @return returns true if a common type is found, false otherwise
|
||||
*/
|
||||
public static boolean areBiomesEquivalent(BiomeGenBase biomeA, BiomeGenBase biomeB)
|
||||
{
|
||||
int a = biomeA.biomeID;
|
||||
int b = biomeB.biomeID;
|
||||
|
||||
checkRegistration(biomeA);
|
||||
checkRegistration(biomeB);
|
||||
|
||||
if(biomeList[a] != null && biomeList[b] != null)
|
||||
{
|
||||
for(Type type : biomeList[a].typeList)
|
||||
{
|
||||
if(containsType(biomeList[b], type))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the given biome is registered as being a specific type
|
||||
*
|
||||
* @param biome the biome to be considered
|
||||
* @param type the type to check for
|
||||
* @return returns true if the biome is registered as being of type type, false otherwise
|
||||
*/
|
||||
public static boolean isBiomeOfType(BiomeGenBase biome, Type type)
|
||||
{
|
||||
checkRegistration(biome);
|
||||
|
||||
if(biomeList[biome.biomeID] != null)
|
||||
{
|
||||
return containsType(biomeList[biome.biomeID], type);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the given biome has been registered as being of any type
|
||||
* @param biome the biome to consider
|
||||
* @return returns true if the biome has been registered, false otherwise
|
||||
*/
|
||||
public static boolean isBiomeRegistered(BiomeGenBase biome)
|
||||
{
|
||||
return biomeList[biome.biomeID] != null;
|
||||
}
|
||||
|
||||
public static boolean isBiomeRegistered(int biomeID)
|
||||
{
|
||||
return biomeList[biomeID] != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loops through the biome list and automatically adds tags to any biome that does not have any
|
||||
* This should be called in postInit to make sure all biomes have been registered
|
||||
*
|
||||
* DO NOT call this during world generation
|
||||
*/
|
||||
public static void registerAllBiomes()
|
||||
{
|
||||
for(int i = 0; i < BIOME_LIST_SIZE; i++)
|
||||
{
|
||||
if(BiomeGenBase.biomeList[i] != null)
|
||||
{
|
||||
checkRegistration(BiomeGenBase.biomeList[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatically looks for and registers a given biome with appropriate tags
|
||||
* This method is called automatically if a biome has not been registered with any tags,
|
||||
* And another method requests information about it
|
||||
*
|
||||
* NOTE: You can opt out of having your biome registered by registering it as type NULL
|
||||
*
|
||||
* @param biome the biome to be considered
|
||||
*/
|
||||
public static void makeBestGuess(BiomeGenBase biome)
|
||||
{
|
||||
if(biome.theBiomeDecorator.treesPerChunk >= 3)
|
||||
{
|
||||
if(biome.isHighHumidity() && biome.temperature >= 1.0F)
|
||||
{
|
||||
BiomeDictionary.registerBiomeType(biome, JUNGLE);
|
||||
}
|
||||
else if(!biome.isHighHumidity())
|
||||
{
|
||||
BiomeDictionary.registerBiomeType(biome, FOREST);
|
||||
}
|
||||
}
|
||||
else if(biome.maxHeight <= 0.3F && biome.maxHeight >= 0.0F)
|
||||
{
|
||||
if(!biome.isHighHumidity() || biome.minHeight >= 0.0F)
|
||||
{
|
||||
BiomeDictionary.registerBiomeType(biome, PLAINS);
|
||||
}
|
||||
}
|
||||
|
||||
if(biome.isHighHumidity() && biome.minHeight < 0.0F && (biome.maxHeight <= 0.3F && biome.maxHeight >= 0.0F))
|
||||
{
|
||||
BiomeDictionary.registerBiomeType(biome, SWAMP);
|
||||
}
|
||||
|
||||
if(biome.minHeight <= -0.5F)
|
||||
{
|
||||
BiomeDictionary.registerBiomeType(biome, WATER);
|
||||
}
|
||||
|
||||
if(biome.maxHeight >= 1.5F)
|
||||
{
|
||||
BiomeDictionary.registerBiomeType(biome, MOUNTAIN);
|
||||
}
|
||||
|
||||
if(biome.getEnableSnow() || biome.temperature < 0.2F)
|
||||
{
|
||||
BiomeDictionary.registerBiomeType(biome, FROZEN);
|
||||
}
|
||||
|
||||
if(!biome.isHighHumidity() && biome.temperature >= 1.0F)
|
||||
{
|
||||
BiomeDictionary.registerBiomeType(biome, DESERT);
|
||||
}
|
||||
}
|
||||
|
||||
//Internal implementation
|
||||
private static void checkRegistration(BiomeGenBase biome)
|
||||
{
|
||||
if(!isBiomeRegistered(biome))
|
||||
{
|
||||
makeBestGuess(biome);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean containsType(BiomeInfo info, Type type)
|
||||
{
|
||||
return info.typeList.contains(type);
|
||||
}
|
||||
|
||||
private static void registerVanillaBiomes()
|
||||
{
|
||||
registerBiomeType(ocean, WATER );
|
||||
registerBiomeType(plains, PLAINS );
|
||||
registerBiomeType(desert, DESERT );
|
||||
registerBiomeType(extremeHills, MOUNTAIN );
|
||||
registerBiomeType(forest, FOREST );
|
||||
registerBiomeType(taiga, FOREST, FROZEN);
|
||||
registerBiomeType(taigaHills, FOREST, FROZEN);
|
||||
registerBiomeType(swampland, SWAMP );
|
||||
registerBiomeType(river, WATER );
|
||||
registerBiomeType(frozenOcean, WATER, FROZEN);
|
||||
registerBiomeType(frozenRiver, WATER, FROZEN);
|
||||
registerBiomeType(icePlains, FROZEN );
|
||||
registerBiomeType(iceMountains, FROZEN );
|
||||
registerBiomeType(beach, BEACH );
|
||||
registerBiomeType(desertHills, DESERT );
|
||||
registerBiomeType(jungle, JUNGLE );
|
||||
registerBiomeType(jungleHills, JUNGLE );
|
||||
registerBiomeType(forestHills, FOREST );
|
||||
registerBiomeType(sky, END );
|
||||
registerBiomeType(hell, NETHER );
|
||||
registerBiomeType(mushroomIsland, MUSHROOM );
|
||||
registerBiomeType(extremeHillsEdge, MOUNTAIN );
|
||||
registerBiomeType(mushroomIslandShore, MUSHROOM, BEACH);
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package net.minecraftforge.common;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import net.minecraft.world.biome.BiomeGenBase;
|
||||
import net.minecraft.world.gen.ChunkProviderGenerate;
|
||||
import net.minecraft.world.chunk.IChunkProvider;
|
||||
import net.minecraft.world.gen.structure.MapGenStronghold;
|
||||
import net.minecraft.world.gen.structure.MapGenVillage;
|
||||
import net.minecraft.world.biome.WorldChunkManager;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
public class BiomeManager
|
||||
{
|
||||
public static void addVillageBiome(BiomeGenBase biome, boolean canSpawn)
|
||||
{
|
||||
if (!MapGenVillage.villageSpawnBiomes.contains(biome))
|
||||
{
|
||||
ArrayList<BiomeGenBase> biomes = new ArrayList<BiomeGenBase>(MapGenVillage.villageSpawnBiomes);
|
||||
biomes.add(biome);
|
||||
MapGenVillage.villageSpawnBiomes = biomes;
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeVillageBiome(BiomeGenBase biome)
|
||||
{
|
||||
if (MapGenVillage.villageSpawnBiomes.contains(biome))
|
||||
{
|
||||
ArrayList<BiomeGenBase> biomes = new ArrayList<BiomeGenBase>(MapGenVillage.villageSpawnBiomes);
|
||||
biomes.remove(biome);
|
||||
MapGenVillage.villageSpawnBiomes = biomes;
|
||||
}
|
||||
}
|
||||
|
||||
public static void addStrongholdBiome(BiomeGenBase biome)
|
||||
{
|
||||
if (!MapGenStronghold.allowedBiomes.contains(biome))
|
||||
{
|
||||
MapGenStronghold.allowedBiomes.add(biome);
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeStrongholdBiome(BiomeGenBase biome)
|
||||
{
|
||||
if (MapGenStronghold.allowedBiomes.contains(biome))
|
||||
{
|
||||
MapGenStronghold.allowedBiomes.remove(biome);
|
||||
}
|
||||
}
|
||||
|
||||
public static void addSpawnBiome(BiomeGenBase biome)
|
||||
{
|
||||
if (!WorldChunkManager.allowedBiomes.contains(biome))
|
||||
{
|
||||
WorldChunkManager.allowedBiomes.add(biome);
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeSpawnBiome(BiomeGenBase biome)
|
||||
{
|
||||
if (WorldChunkManager.allowedBiomes.contains(biome))
|
||||
{
|
||||
WorldChunkManager.allowedBiomes.remove(biome);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,260 +0,0 @@
|
||||
package net.minecraftforge.common;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.WeightedRandom;
|
||||
import net.minecraft.util.WeightedRandomChestContent;
|
||||
import net.minecraft.world.WorldServer;
|
||||
import net.minecraft.world.gen.structure.*;
|
||||
import net.minecraftforge.oredict.OreDictionary;
|
||||
|
||||
public class ChestGenHooks
|
||||
{
|
||||
//Currently implemented categories for chests/dispensers, Dungeon loot is still in DungeonHooks
|
||||
public static final String MINESHAFT_CORRIDOR = "mineshaftCorridor";
|
||||
public static final String PYRAMID_DESERT_CHEST = "pyramidDesertyChest";
|
||||
public static final String PYRAMID_JUNGLE_CHEST = "pyramidJungleChest";
|
||||
public static final String PYRAMID_JUNGLE_DISPENSER = "pyramidJungleDispenser";
|
||||
public static final String STRONGHOLD_CORRIDOR = "strongholdCorridor";
|
||||
public static final String STRONGHOLD_LIBRARY = "strongholdLibrary";
|
||||
public static final String STRONGHOLD_CROSSING = "strongholdCrossing";
|
||||
public static final String VILLAGE_BLACKSMITH = "villageBlacksmith";
|
||||
public static final String BONUS_CHEST = "bonusChest";
|
||||
public static final String DUNGEON_CHEST = "dungeonChest";
|
||||
|
||||
private static final HashMap<String, ChestGenHooks> chestInfo = new HashMap<String, ChestGenHooks>();
|
||||
private static boolean hasInit = false;
|
||||
static
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
private static void init()
|
||||
{
|
||||
if (hasInit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
hasInit = true;
|
||||
|
||||
addInfo(MINESHAFT_CORRIDOR, StructureMineshaftPieces.mineshaftChestContents, 3, 7);
|
||||
addInfo(PYRAMID_DESERT_CHEST, ComponentScatteredFeatureDesertPyramid.itemsToGenerateInTemple, 2, 7);
|
||||
addInfo(PYRAMID_JUNGLE_CHEST, ComponentScatteredFeatureJunglePyramid.junglePyramidsChestContents, 2, 7);
|
||||
addInfo(PYRAMID_JUNGLE_DISPENSER, ComponentScatteredFeatureJunglePyramid.junglePyramidsDispenserContents, 2, 2);
|
||||
addInfo(STRONGHOLD_CORRIDOR, ComponentStrongholdChestCorridor.strongholdChestContents, 2, 4);
|
||||
addInfo(STRONGHOLD_LIBRARY, ComponentStrongholdLibrary.strongholdLibraryChestContents, 1, 5);
|
||||
addInfo(STRONGHOLD_CROSSING, ComponentStrongholdRoomCrossing.strongholdRoomCrossingChestContents, 1, 5);
|
||||
addInfo(VILLAGE_BLACKSMITH, ComponentVillageHouse2.villageBlacksmithChestContents, 3, 9);
|
||||
addInfo(BONUS_CHEST, WorldServer.bonusChestContent, 10, 10);
|
||||
|
||||
ItemStack book = new ItemStack(Item.enchantedBook, 1, 0);
|
||||
WeightedRandomChestContent tmp = new WeightedRandomChestContent(book, 1, 1, 1);
|
||||
getInfo(MINESHAFT_CORRIDOR ).addItem(tmp);
|
||||
getInfo(PYRAMID_DESERT_CHEST).addItem(tmp);
|
||||
getInfo(PYRAMID_JUNGLE_CHEST).addItem(tmp);
|
||||
getInfo(STRONGHOLD_CORRIDOR ).addItem(tmp);
|
||||
getInfo(STRONGHOLD_LIBRARY ).addItem(new WeightedRandomChestContent(book, 1, 5, 2));
|
||||
getInfo(STRONGHOLD_CROSSING ).addItem(tmp);
|
||||
|
||||
//Wish Dungeons would get on the same wave length as other world gen...
|
||||
ChestGenHooks d = new ChestGenHooks(DUNGEON_CHEST);
|
||||
d.countMin = 8;
|
||||
d.countMax = 8;
|
||||
chestInfo.put(DUNGEON_CHEST, d);
|
||||
addDungeonLoot(d, new ItemStack(Item.saddle), 100, 1, 1);
|
||||
addDungeonLoot(d, new ItemStack(Item.ingotIron), 100, 1, 4);
|
||||
addDungeonLoot(d, new ItemStack(Item.bread), 100, 1, 1);
|
||||
addDungeonLoot(d, new ItemStack(Item.wheat), 100, 1, 4);
|
||||
addDungeonLoot(d, new ItemStack(Item.gunpowder), 100, 1, 4);
|
||||
addDungeonLoot(d, new ItemStack(Item.silk), 100, 1, 4);
|
||||
addDungeonLoot(d, new ItemStack(Item.bucketEmpty), 100, 1, 1);
|
||||
addDungeonLoot(d, new ItemStack(Item.appleGold), 1, 1, 1);
|
||||
addDungeonLoot(d, new ItemStack(Item.redstone), 50, 1, 4);
|
||||
addDungeonLoot(d, new ItemStack(Item.record13), 5, 1, 1);
|
||||
addDungeonLoot(d, new ItemStack(Item.recordCat), 5, 1, 1);
|
||||
addDungeonLoot(d, new ItemStack(Item.dyePowder, 1, 3), 100, 1, 1);
|
||||
addDungeonLoot(d, book, 100, 1, 1);
|
||||
}
|
||||
|
||||
static void addDungeonLoot(ChestGenHooks dungeon, ItemStack item, int weight, int min, int max)
|
||||
{
|
||||
dungeon.addItem(new WeightedRandomChestContent(item, min, max, weight));
|
||||
}
|
||||
|
||||
private static void addInfo(String category, WeightedRandomChestContent[] items, int min, int max)
|
||||
{
|
||||
chestInfo.put(category, new ChestGenHooks(category, items, min, max));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves, or creates the info class for the specified category.
|
||||
*
|
||||
* @param category The category name
|
||||
* @return A instance of ChestGenHooks for the specified category.
|
||||
*/
|
||||
public static ChestGenHooks getInfo(String category)
|
||||
{
|
||||
if (!chestInfo.containsKey(category))
|
||||
{
|
||||
chestInfo.put(category, new ChestGenHooks(category));
|
||||
}
|
||||
return chestInfo.get(category);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an array of items based on the input min/max count.
|
||||
* If the stack can not hold the total amount, it will be split into
|
||||
* stacks of size 1.
|
||||
*
|
||||
* @param rand A random number generator
|
||||
* @param source Source item stack
|
||||
* @param min Minimum number of items
|
||||
* @param max Maximum number of items
|
||||
* @return An array containing the generated item stacks
|
||||
*/
|
||||
public static ItemStack[] generateStacks(Random rand, ItemStack source, int min, int max)
|
||||
{
|
||||
int count = min + (rand.nextInt(max - min + 1));
|
||||
|
||||
ItemStack[] ret;
|
||||
if (source.getItem() == null)
|
||||
{
|
||||
ret = new ItemStack[0];
|
||||
}
|
||||
else if (count > source.getItem().getItemStackLimit())
|
||||
{
|
||||
ret = new ItemStack[count];
|
||||
for (int x = 0; x < count; x++)
|
||||
{
|
||||
ret[x] = source.copy();
|
||||
ret[x].stackSize = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = new ItemStack[1];
|
||||
ret[0] = source.copy();
|
||||
ret[0].stackSize = count;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
//shortcut functions, See the non-static versions below
|
||||
public static WeightedRandomChestContent[] getItems(String category, Random rnd){ return getInfo(category).getItems(rnd); }
|
||||
public static int getCount(String category, Random rand){ return getInfo(category).getCount(rand); }
|
||||
public static void addItem(String category, WeightedRandomChestContent item){ getInfo(category).addItem(item); }
|
||||
public static void removeItem(String category, ItemStack item){ getInfo(category).removeItem(item); }
|
||||
public static ItemStack getOneItem(String category, Random rand){ return getInfo(category).getOneItem(rand); }
|
||||
|
||||
private String category;
|
||||
private int countMin = 0;
|
||||
private int countMax = 0;
|
||||
//TO-DO: Privatize this once again when we remove the Deprecated stuff in DungeonHooks
|
||||
ArrayList<WeightedRandomChestContent> contents = new ArrayList<WeightedRandomChestContent>();
|
||||
|
||||
public ChestGenHooks(String category)
|
||||
{
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
public ChestGenHooks(String category, WeightedRandomChestContent[] items, int min, int max)
|
||||
{
|
||||
this(category);
|
||||
for (WeightedRandomChestContent item : items)
|
||||
{
|
||||
contents.add(item);
|
||||
}
|
||||
countMin = min;
|
||||
countMax = max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new entry into the possible items to generate.
|
||||
*
|
||||
* @param item The item to add.
|
||||
*/
|
||||
public void addItem(WeightedRandomChestContent item)
|
||||
{
|
||||
contents.add(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all items that match the input item stack, Only metadata and item ID are checked.
|
||||
* If the input item has a metadata of -1, all metadatas will match.
|
||||
*
|
||||
* @param item The item to check
|
||||
*/
|
||||
public void removeItem(ItemStack item)
|
||||
{
|
||||
Iterator<WeightedRandomChestContent> itr = contents.iterator();
|
||||
while(itr.hasNext())
|
||||
{
|
||||
WeightedRandomChestContent cont = itr.next();
|
||||
if (item.isItemEqual(cont.theItemId) || (item.getItemDamage() == OreDictionary.WILDCARD_VALUE && item.itemID == cont.theItemId.itemID))
|
||||
{
|
||||
itr.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an array of all random objects that are associated with this category.
|
||||
*
|
||||
* @return The random objects
|
||||
*/
|
||||
public WeightedRandomChestContent[] getItems(Random rnd)
|
||||
{
|
||||
ArrayList<WeightedRandomChestContent> ret = new ArrayList<WeightedRandomChestContent>();
|
||||
|
||||
for (WeightedRandomChestContent orig : contents)
|
||||
{
|
||||
Item item = orig.theItemId.getItem();
|
||||
|
||||
if (item != null)
|
||||
{
|
||||
WeightedRandomChestContent n = item.getChestGenBase(this, rnd, orig);
|
||||
if (n != null)
|
||||
{
|
||||
ret.add(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret.toArray(new WeightedRandomChestContent[ret.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a random number between countMin and countMax.
|
||||
*
|
||||
* @param rand A RNG
|
||||
* @return A random number where countMin <= num <= countMax
|
||||
*/
|
||||
public int getCount(Random rand)
|
||||
{
|
||||
return countMin < countMax ? countMin + rand.nextInt(countMax - countMin) : countMin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a single ItemStack from the possible items in this registry,
|
||||
* Useful if you just want a quick and dirty random Item.
|
||||
*
|
||||
* @param rand A Random Number gen
|
||||
* @return A single ItemStack, or null if it could not get one.
|
||||
*/
|
||||
public ItemStack getOneItem(Random rand)
|
||||
{
|
||||
WeightedRandomChestContent[] items = getItems(rand);
|
||||
WeightedRandomChestContent item = (WeightedRandomChestContent)WeightedRandom.getRandomItem(rand, items);
|
||||
ItemStack[] stacks = ChestGenHooks.generateStacks(rand, item.theItemId, item.theMinimumChanceToGenerateItem, item.theMaximumChanceToGenerateItem);
|
||||
return (stacks.length > 0 ? stacks[0] : null);
|
||||
}
|
||||
|
||||
//Accessors
|
||||
public int getMin(){ return countMin; }
|
||||
public int getMax(){ return countMax; }
|
||||
public void setMin(int value){ countMin = value; }
|
||||
public void setMax(int value){ countMax = value; }
|
||||
}
|
||||
@@ -1,269 +0,0 @@
|
||||
package net.minecraftforge.common;
|
||||
|
||||
import static net.minecraftforge.common.Configuration.NEW_LINE;
|
||||
import static net.minecraftforge.common.Configuration.allowedProperties;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.base.Splitter;
|
||||
|
||||
public class ConfigCategory implements Map<String, Property>
|
||||
{
|
||||
private String name;
|
||||
private String comment;
|
||||
private ArrayList<ConfigCategory> children = new ArrayList<ConfigCategory>();
|
||||
private Map<String, Property> properties = new TreeMap<String, Property>();
|
||||
public final ConfigCategory parent;
|
||||
private boolean changed = false;
|
||||
|
||||
public ConfigCategory(String name)
|
||||
{
|
||||
this(name, null);
|
||||
}
|
||||
|
||||
public ConfigCategory(String name, ConfigCategory parent)
|
||||
{
|
||||
this.name = name;
|
||||
this.parent = parent;
|
||||
if (parent != null)
|
||||
{
|
||||
parent.children.add(this);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (obj instanceof ConfigCategory)
|
||||
{
|
||||
ConfigCategory cat = (ConfigCategory)obj;
|
||||
return name.equals(cat.name) && children.equals(cat.children);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getQualifiedName()
|
||||
{
|
||||
return getQualifiedName(name, parent);
|
||||
}
|
||||
|
||||
public static String getQualifiedName(String name, ConfigCategory parent)
|
||||
{
|
||||
return (parent == null ? name : parent.getQualifiedName() + Configuration.CATEGORY_SPLITTER + name);
|
||||
}
|
||||
|
||||
public ConfigCategory getFirstParent()
|
||||
{
|
||||
return (parent == null ? this : parent.getFirstParent());
|
||||
}
|
||||
|
||||
public boolean isChild()
|
||||
{
|
||||
return parent != null;
|
||||
}
|
||||
|
||||
public Map<String, Property> getValues()
|
||||
{
|
||||
return ImmutableMap.copyOf(properties);
|
||||
}
|
||||
|
||||
public void setComment(String comment)
|
||||
{
|
||||
this.comment = comment;
|
||||
}
|
||||
|
||||
public boolean containsKey(String key)
|
||||
{
|
||||
return properties.containsKey(key);
|
||||
}
|
||||
|
||||
public Property get(String key)
|
||||
{
|
||||
return properties.get(key);
|
||||
}
|
||||
|
||||
private void write(BufferedWriter out, String... data) throws IOException
|
||||
{
|
||||
write(out, true, data);
|
||||
}
|
||||
|
||||
private void write(BufferedWriter out, boolean new_line, String... data) throws IOException
|
||||
{
|
||||
for (int x = 0; x < data.length; x++)
|
||||
{
|
||||
out.write(data[x]);
|
||||
}
|
||||
if (new_line) out.write(NEW_LINE);
|
||||
}
|
||||
|
||||
public void write(BufferedWriter out, int indent) throws IOException
|
||||
{
|
||||
String pad0 = getIndent(indent);
|
||||
String pad1 = getIndent(indent + 1);
|
||||
String pad2 = getIndent(indent + 2);
|
||||
|
||||
write(out, pad0, "####################");
|
||||
write(out, pad0, "# ", name);
|
||||
|
||||
if (comment != null)
|
||||
{
|
||||
write(out, pad0, "#===================");
|
||||
Splitter splitter = Splitter.onPattern("\r?\n");
|
||||
|
||||
for (String line : splitter.split(comment))
|
||||
{
|
||||
write(out, pad0, "# ", line);
|
||||
}
|
||||
}
|
||||
|
||||
write(out, pad0, "####################", NEW_LINE);
|
||||
|
||||
if (!allowedProperties.matchesAllOf(name))
|
||||
{
|
||||
name = '"' + name + '"';
|
||||
}
|
||||
|
||||
write(out, pad0, name, " {");
|
||||
|
||||
Property[] props = properties.values().toArray(new Property[properties.size()]);
|
||||
|
||||
for (int x = 0; x < props.length; x++)
|
||||
{
|
||||
Property prop = props[x];
|
||||
|
||||
if (prop.comment != null)
|
||||
{
|
||||
if (x != 0)
|
||||
{
|
||||
out.newLine();
|
||||
}
|
||||
|
||||
Splitter splitter = Splitter.onPattern("\r?\n");
|
||||
for (String commentLine : splitter.split(prop.comment))
|
||||
{
|
||||
write(out, pad1, "# ", commentLine);
|
||||
}
|
||||
}
|
||||
|
||||
String propName = prop.getName();
|
||||
|
||||
if (!allowedProperties.matchesAllOf(propName))
|
||||
{
|
||||
propName = '"' + propName + '"';
|
||||
}
|
||||
|
||||
if (prop.isList())
|
||||
{
|
||||
char type = prop.getType().getID();
|
||||
|
||||
write(out, pad1, String.valueOf(type), ":", propName, " <");
|
||||
|
||||
for (String line : prop.getStringList())
|
||||
{
|
||||
write(out, pad2, line);
|
||||
}
|
||||
|
||||
write(out, pad1, " >");
|
||||
}
|
||||
else if (prop.getType() == null)
|
||||
{
|
||||
write(out, pad1, propName, "=", prop.getString());
|
||||
}
|
||||
else
|
||||
{
|
||||
char type = prop.getType().getID();
|
||||
write(out, pad1, String.valueOf(type), ":", propName, "=", prop.getString());
|
||||
}
|
||||
}
|
||||
|
||||
for (ConfigCategory child : children)
|
||||
{
|
||||
child.write(out, indent + 1);
|
||||
}
|
||||
|
||||
write(out, pad0, "}", NEW_LINE);
|
||||
}
|
||||
|
||||
private String getIndent(int indent)
|
||||
{
|
||||
StringBuilder buf = new StringBuilder("");
|
||||
for (int x = 0; x < indent; x++)
|
||||
{
|
||||
buf.append(" ");
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public boolean hasChanged()
|
||||
{
|
||||
if (changed) return true;
|
||||
for (Property prop : properties.values())
|
||||
{
|
||||
if (prop.hasChanged()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void resetChangedState()
|
||||
{
|
||||
changed = false;
|
||||
for (Property prop : properties.values())
|
||||
{
|
||||
prop.resetChangedState();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Map bouncer functions for compatibility with older mods, to be removed once all mods stop using it.
|
||||
@Override public int size(){ return properties.size(); }
|
||||
@Override public boolean isEmpty() { return properties.isEmpty(); }
|
||||
@Override public boolean containsKey(Object key) { return properties.containsKey(key); }
|
||||
@Override public boolean containsValue(Object value){ return properties.containsValue(value); }
|
||||
@Override public Property get(Object key) { return properties.get(key); }
|
||||
@Override public Property put(String key, Property value)
|
||||
{
|
||||
changed = true;
|
||||
return properties.put(key, value);
|
||||
}
|
||||
@Override public Property remove(Object key)
|
||||
{
|
||||
changed = true;
|
||||
return properties.remove(key);
|
||||
}
|
||||
@Override public void putAll(Map<? extends String, ? extends Property> m)
|
||||
{
|
||||
changed = true;
|
||||
properties.putAll(m);
|
||||
}
|
||||
@Override public void clear()
|
||||
{
|
||||
changed = true;
|
||||
properties.clear();
|
||||
}
|
||||
@Override public Set<String> keySet() { return properties.keySet(); }
|
||||
@Override public Collection<Property> values() { return properties.values(); }
|
||||
|
||||
@Override //Immutable copy, changes will NOT be reflected in this category
|
||||
public Set<java.util.Map.Entry<String, Property>> entrySet()
|
||||
{
|
||||
return ImmutableSet.copyOf(properties.entrySet());
|
||||
}
|
||||
|
||||
public Set<ConfigCategory> getChildren(){ return ImmutableSet.copyOf(children); }
|
||||
|
||||
public void removeChild(ConfigCategory child)
|
||||
{
|
||||
if (children.contains(child))
|
||||
{
|
||||
children.remove(child);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,927 +0,0 @@
|
||||
/**
|
||||
* This software is provided under the terms of the Minecraft Forge Public
|
||||
* License v1.0.
|
||||
*/
|
||||
|
||||
package net.minecraftforge.common;
|
||||
|
||||
import static net.minecraftforge.common.Property.Type.BOOLEAN;
|
||||
import static net.minecraftforge.common.Property.Type.DOUBLE;
|
||||
import static net.minecraftforge.common.Property.Type.INTEGER;
|
||||
import static net.minecraftforge.common.Property.Type.STRING;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PushbackInputStream;
|
||||
import java.io.Reader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.item.Item;
|
||||
|
||||
import com.google.common.base.CharMatcher;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
import cpw.mods.fml.common.FMLLog;
|
||||
import cpw.mods.fml.common.Loader;
|
||||
import cpw.mods.fml.relauncher.FMLInjectionData;
|
||||
|
||||
/**
|
||||
* This class offers advanced configurations capabilities, allowing to provide
|
||||
* various categories for configuration variables.
|
||||
*/
|
||||
public class Configuration
|
||||
{
|
||||
private static boolean[] configMarkers = new boolean[Item.itemsList.length];
|
||||
private static final int ITEM_SHIFT = 256;
|
||||
private static final int MAX_BLOCKS = 4096;
|
||||
|
||||
public static final String CATEGORY_GENERAL = "general";
|
||||
public static final String CATEGORY_BLOCK = "block";
|
||||
public static final String CATEGORY_ITEM = "item";
|
||||
public static final String ALLOWED_CHARS = "._-";
|
||||
public static final String DEFAULT_ENCODING = "UTF-8";
|
||||
public static final String CATEGORY_SPLITTER = ".";
|
||||
public static final String NEW_LINE;
|
||||
private static final Pattern CONFIG_START = Pattern.compile("START: \"([^\\\"]+)\"");
|
||||
private static final Pattern CONFIG_END = Pattern.compile("END: \"([^\\\"]+)\"");
|
||||
public static final CharMatcher allowedProperties = CharMatcher.JAVA_LETTER_OR_DIGIT.or(CharMatcher.anyOf(ALLOWED_CHARS));
|
||||
private static Configuration PARENT = null;
|
||||
|
||||
File file;
|
||||
|
||||
private Map<String, ConfigCategory> categories = new TreeMap<String, ConfigCategory>();
|
||||
private Map<String, Configuration> children = new TreeMap<String, Configuration>();
|
||||
|
||||
private boolean caseSensitiveCustomCategories;
|
||||
public String defaultEncoding = DEFAULT_ENCODING;
|
||||
private String fileName = null;
|
||||
public boolean isChild = false;
|
||||
private boolean changed = false;
|
||||
|
||||
static
|
||||
{
|
||||
Arrays.fill(configMarkers, false);
|
||||
NEW_LINE = System.getProperty("line.separator");
|
||||
}
|
||||
|
||||
public Configuration(){}
|
||||
|
||||
/**
|
||||
* Create a configuration file for the file given in parameter.
|
||||
*/
|
||||
public Configuration(File file)
|
||||
{
|
||||
this.file = file;
|
||||
String basePath = ((File)(FMLInjectionData.data()[6])).getAbsolutePath().replace(File.separatorChar, '/').replace("/.", "");
|
||||
String path = file.getAbsolutePath().replace(File.separatorChar, '/').replace("/./", "/").replace(basePath, "");
|
||||
if (PARENT != null)
|
||||
{
|
||||
PARENT.setChild(path, this);
|
||||
isChild = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
fileName = path;
|
||||
load();
|
||||
}
|
||||
}
|
||||
|
||||
public Configuration(File file, boolean caseSensitiveCustomCategories)
|
||||
{
|
||||
this(file);
|
||||
this.caseSensitiveCustomCategories = caseSensitiveCustomCategories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets or create a block id property. If the block id property key is
|
||||
* already in the configuration, then it will be used. Otherwise,
|
||||
* defaultId will be used, except if already taken, in which case this
|
||||
* will try to determine a free default id.
|
||||
*/
|
||||
public Property getBlock(String key, int defaultID) { return getBlock(CATEGORY_BLOCK, key, defaultID, null); }
|
||||
public Property getBlock(String key, int defaultID, String comment) { return getBlock(CATEGORY_BLOCK, key, defaultID, comment); }
|
||||
public Property getBlock(String category, String key, int defaultID) { return getBlockInternal(category, key, defaultID, null, 256, Block.blocksList.length); }
|
||||
public Property getBlock(String category, String key, int defaultID, String comment) { return getBlockInternal(category, key, defaultID, comment, 256, Block.blocksList.length); }
|
||||
|
||||
/**
|
||||
* Special version of getBlock to be used when you want to garentee the ID you get is below 256
|
||||
* This should ONLY be used by mods who do low level terrain generation, or ones that add new
|
||||
* biomes.
|
||||
* EXA: ExtraBiomesXL
|
||||
*
|
||||
* Specifically, if your block is used BEFORE the Chunk is created, and placed in the terrain byte array directly.
|
||||
* If you add a new biome and you set the top/filler block, they need to be <256, nothing else.
|
||||
*
|
||||
* If you're adding a new ore, DON'T call this function.
|
||||
*
|
||||
* Normal mods such as '50 new ores' do not need to be below 256 so should use the normal getBlock
|
||||
*/
|
||||
public Property getTerrainBlock(String category, String key, int defaultID, String comment)
|
||||
{
|
||||
return getBlockInternal(category, key, defaultID, comment, 0, 256);
|
||||
}
|
||||
|
||||
private Property getBlockInternal(String category, String key, int defaultID, String comment, int lower, int upper)
|
||||
{
|
||||
Property prop = get(category, key, -1, comment);
|
||||
|
||||
if (prop.getInt() != -1)
|
||||
{
|
||||
configMarkers[prop.getInt()] = true;
|
||||
return prop;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (defaultID < lower)
|
||||
{
|
||||
FMLLog.warning(
|
||||
"Mod attempted to get a block ID with a default in the Terrain Generation section, " +
|
||||
"mod authors should make sure there defaults are above 256 unless explicitly needed " +
|
||||
"for terrain generation. Most ores do not need to be below 256.");
|
||||
FMLLog.warning("Config \"%s\" Category: \"%s\" Key: \"%s\" Default: %d", fileName, category, key, defaultID);
|
||||
defaultID = upper - 1;
|
||||
}
|
||||
|
||||
if (Block.blocksList[defaultID] == null && !configMarkers[defaultID])
|
||||
{
|
||||
prop.set(defaultID);
|
||||
configMarkers[defaultID] = true;
|
||||
return prop;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int j = upper - 1; j > 0; j--)
|
||||
{
|
||||
if (Block.blocksList[j] == null && !configMarkers[j])
|
||||
{
|
||||
prop.set(j);
|
||||
configMarkers[j] = true;
|
||||
return prop;
|
||||
}
|
||||
}
|
||||
|
||||
throw new RuntimeException("No more block ids available for " + key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Property getItem(String key, int defaultID) { return getItem(CATEGORY_ITEM, key, defaultID, null); }
|
||||
public Property getItem(String key, int defaultID, String comment) { return getItem(CATEGORY_ITEM, key, defaultID, comment); }
|
||||
public Property getItem(String category, String key, int defaultID) { return getItem(category, key, defaultID, null); }
|
||||
|
||||
public Property getItem(String category, String key, int defaultID, String comment)
|
||||
{
|
||||
Property prop = get(category, key, -1, comment);
|
||||
int defaultShift = defaultID + ITEM_SHIFT;
|
||||
|
||||
if (prop.getInt() != -1)
|
||||
{
|
||||
configMarkers[prop.getInt() + ITEM_SHIFT] = true;
|
||||
return prop;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (defaultID < MAX_BLOCKS - ITEM_SHIFT)
|
||||
{
|
||||
FMLLog.warning(
|
||||
"Mod attempted to get a item ID with a default value in the block ID section, " +
|
||||
"mod authors should make sure there defaults are above %d unless explicitly needed " +
|
||||
"so that all block ids are free to store blocks.", MAX_BLOCKS - ITEM_SHIFT);
|
||||
FMLLog.warning("Config \"%s\" Category: \"%s\" Key: \"%s\" Default: %d", fileName, category, key, defaultID);
|
||||
}
|
||||
|
||||
if (Item.itemsList[defaultShift] == null && !configMarkers[defaultShift] && defaultShift >= Block.blocksList.length)
|
||||
{
|
||||
prop.set(defaultID);
|
||||
configMarkers[defaultShift] = true;
|
||||
return prop;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int x = Item.itemsList.length - 1; x >= ITEM_SHIFT; x--)
|
||||
{
|
||||
if (Item.itemsList[x] == null && !configMarkers[x])
|
||||
{
|
||||
prop.set(x - ITEM_SHIFT);
|
||||
configMarkers[x] = true;
|
||||
return prop;
|
||||
}
|
||||
}
|
||||
|
||||
throw new RuntimeException("No more item ids available for " + key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Property get(String category, String key, int defaultValue)
|
||||
{
|
||||
return get(category, key, defaultValue, null);
|
||||
}
|
||||
|
||||
public Property get(String category, String key, int defaultValue, String comment)
|
||||
{
|
||||
Property prop = get(category, key, Integer.toString(defaultValue), comment, INTEGER);
|
||||
if (!prop.isIntValue())
|
||||
{
|
||||
prop.set(defaultValue);
|
||||
}
|
||||
return prop;
|
||||
}
|
||||
|
||||
public Property get(String category, String key, boolean defaultValue)
|
||||
{
|
||||
return get(category, key, defaultValue, null);
|
||||
}
|
||||
|
||||
public Property get(String category, String key, boolean defaultValue, String comment)
|
||||
{
|
||||
Property prop = get(category, key, Boolean.toString(defaultValue), comment, BOOLEAN);
|
||||
if (!prop.isBooleanValue())
|
||||
{
|
||||
prop.set(defaultValue);
|
||||
}
|
||||
return prop;
|
||||
}
|
||||
|
||||
public Property get(String category, String key, double defaultValue)
|
||||
{
|
||||
return get(category, key, defaultValue, null);
|
||||
}
|
||||
|
||||
public Property get(String category, String key, double defaultValue, String comment)
|
||||
{
|
||||
Property prop = get(category, key, Double.toString(defaultValue), comment, DOUBLE);
|
||||
if (!prop.isDoubleValue())
|
||||
{
|
||||
prop.set(defaultValue);
|
||||
}
|
||||
return prop;
|
||||
}
|
||||
|
||||
public Property get(String category, String key, String defaultValue)
|
||||
{
|
||||
return get(category, key, defaultValue, null);
|
||||
}
|
||||
|
||||
public Property get(String category, String key, String defaultValue, String comment)
|
||||
{
|
||||
return get(category, key, defaultValue, comment, STRING);
|
||||
}
|
||||
|
||||
public Property get(String category, String key, String[] defaultValue)
|
||||
{
|
||||
return get(category, key, defaultValue, null);
|
||||
}
|
||||
|
||||
public Property get(String category, String key, String[] defaultValue, String comment)
|
||||
{
|
||||
return get(category, key, defaultValue, comment, STRING);
|
||||
}
|
||||
|
||||
public Property get(String category, String key, int[] defaultValue)
|
||||
{
|
||||
return get(category, key, defaultValue, null);
|
||||
}
|
||||
|
||||
public Property get(String category, String key, int[] defaultValue, String comment)
|
||||
{
|
||||
String[] values = new String[defaultValue.length];
|
||||
for (int i = 0; i < defaultValue.length; i++)
|
||||
{
|
||||
values[i] = Integer.toString(defaultValue[i]);
|
||||
}
|
||||
|
||||
Property prop = get(category, key, values, comment, INTEGER);
|
||||
if (!prop.isIntList())
|
||||
{
|
||||
prop.set(values);
|
||||
}
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
public Property get(String category, String key, double[] defaultValue)
|
||||
{
|
||||
return get(category, key, defaultValue, null);
|
||||
}
|
||||
|
||||
public Property get(String category, String key, double[] defaultValue, String comment)
|
||||
{
|
||||
String[] values = new String[defaultValue.length];
|
||||
for (int i = 0; i < defaultValue.length; i++)
|
||||
{
|
||||
values[i] = Double.toString(defaultValue[i]);
|
||||
}
|
||||
|
||||
Property prop = get(category, key, values, comment, DOUBLE);
|
||||
|
||||
if (!prop.isDoubleList())
|
||||
{
|
||||
prop.set(values);
|
||||
}
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
public Property get(String category, String key, boolean[] defaultValue)
|
||||
{
|
||||
return get(category, key, defaultValue, null);
|
||||
}
|
||||
|
||||
public Property get(String category, String key, boolean[] defaultValue, String comment)
|
||||
{
|
||||
String[] values = new String[defaultValue.length];
|
||||
for (int i = 0; i < defaultValue.length; i++)
|
||||
{
|
||||
values[i] = Boolean.toString(defaultValue[i]);
|
||||
}
|
||||
|
||||
Property prop = get(category, key, values, comment, BOOLEAN);
|
||||
|
||||
if (!prop.isBooleanList())
|
||||
{
|
||||
prop.set(values);
|
||||
}
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
public Property get(String category, String key, String defaultValue, String comment, Property.Type type)
|
||||
{
|
||||
if (!caseSensitiveCustomCategories)
|
||||
{
|
||||
category = category.toLowerCase(Locale.ENGLISH);
|
||||
}
|
||||
|
||||
ConfigCategory cat = getCategory(category);
|
||||
|
||||
if (cat.containsKey(key))
|
||||
{
|
||||
Property prop = cat.get(key);
|
||||
|
||||
if (prop.getType() == null)
|
||||
{
|
||||
prop = new Property(prop.getName(), prop.getString(), type);
|
||||
cat.put(key, prop);
|
||||
}
|
||||
|
||||
prop.comment = comment;
|
||||
return prop;
|
||||
}
|
||||
else if (defaultValue != null)
|
||||
{
|
||||
Property prop = new Property(key, defaultValue, type);
|
||||
prop.set(defaultValue); //Set and mark as dirty to signify it should save
|
||||
cat.put(key, prop);
|
||||
prop.comment = comment;
|
||||
return prop;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Property get(String category, String key, String[] defaultValue, String comment, Property.Type type)
|
||||
{
|
||||
if (!caseSensitiveCustomCategories)
|
||||
{
|
||||
category = category.toLowerCase(Locale.ENGLISH);
|
||||
}
|
||||
|
||||
ConfigCategory cat = getCategory(category);
|
||||
|
||||
if (cat.containsKey(key))
|
||||
{
|
||||
Property prop = cat.get(key);
|
||||
|
||||
if (prop.getType() == null)
|
||||
{
|
||||
prop = new Property(prop.getName(), prop.getString(), type);
|
||||
cat.put(key, prop);
|
||||
}
|
||||
|
||||
prop.comment = comment;
|
||||
|
||||
return prop;
|
||||
}
|
||||
else if (defaultValue != null)
|
||||
{
|
||||
Property prop = new Property(key, defaultValue, type);
|
||||
prop.comment = comment;
|
||||
cat.put(key, prop);
|
||||
return prop;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasCategory(String category)
|
||||
{
|
||||
return categories.get(category) != null;
|
||||
}
|
||||
|
||||
public boolean hasKey(String category, String key)
|
||||
{
|
||||
ConfigCategory cat = categories.get(category);
|
||||
return cat != null && cat.containsKey(key);
|
||||
}
|
||||
|
||||
public void load()
|
||||
{
|
||||
if (PARENT != null && PARENT != this)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BufferedReader buffer = null;
|
||||
UnicodeInputStreamReader input = null;
|
||||
try
|
||||
{
|
||||
if (file.getParentFile() != null)
|
||||
{
|
||||
file.getParentFile().mkdirs();
|
||||
}
|
||||
|
||||
if (!file.exists() && !file.createNewFile())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (file.canRead())
|
||||
{
|
||||
input = new UnicodeInputStreamReader(new FileInputStream(file), defaultEncoding);
|
||||
defaultEncoding = input.getEncoding();
|
||||
buffer = new BufferedReader(input);
|
||||
|
||||
String line;
|
||||
ConfigCategory currentCat = null;
|
||||
Property.Type type = null;
|
||||
ArrayList<String> tmpList = null;
|
||||
int lineNum = 0;
|
||||
String name = null;
|
||||
|
||||
while (true)
|
||||
{
|
||||
lineNum++;
|
||||
line = buffer.readLine();
|
||||
|
||||
if (line == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
Matcher start = CONFIG_START.matcher(line);
|
||||
Matcher end = CONFIG_END.matcher(line);
|
||||
|
||||
if (start.matches())
|
||||
{
|
||||
fileName = start.group(1);
|
||||
categories = new TreeMap<String, ConfigCategory>();
|
||||
continue;
|
||||
}
|
||||
else if (end.matches())
|
||||
{
|
||||
fileName = end.group(1);
|
||||
Configuration child = new Configuration();
|
||||
child.categories = categories;
|
||||
this.children.put(fileName, child);
|
||||
continue;
|
||||
}
|
||||
|
||||
int nameStart = -1, nameEnd = -1;
|
||||
boolean skip = false;
|
||||
boolean quoted = false;
|
||||
|
||||
for (int i = 0; i < line.length() && !skip; ++i)
|
||||
{
|
||||
if (Character.isLetterOrDigit(line.charAt(i)) || ALLOWED_CHARS.indexOf(line.charAt(i)) != -1 || (quoted && line.charAt(i) != '"'))
|
||||
{
|
||||
if (nameStart == -1)
|
||||
{
|
||||
nameStart = i;
|
||||
}
|
||||
|
||||
nameEnd = i;
|
||||
}
|
||||
else if (Character.isWhitespace(line.charAt(i)))
|
||||
{
|
||||
// ignore space charaters
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (line.charAt(i))
|
||||
{
|
||||
case '#':
|
||||
skip = true;
|
||||
continue;
|
||||
|
||||
case '"':
|
||||
if (quoted)
|
||||
{
|
||||
quoted = false;
|
||||
}
|
||||
if (!quoted && nameStart == -1)
|
||||
{
|
||||
quoted = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case '{':
|
||||
name = line.substring(nameStart, nameEnd + 1);
|
||||
String qualifiedName = ConfigCategory.getQualifiedName(name, currentCat);
|
||||
|
||||
ConfigCategory cat = categories.get(qualifiedName);
|
||||
if (cat == null)
|
||||
{
|
||||
currentCat = new ConfigCategory(name, currentCat);
|
||||
categories.put(qualifiedName, currentCat);
|
||||
}
|
||||
else
|
||||
{
|
||||
currentCat = cat;
|
||||
}
|
||||
name = null;
|
||||
|
||||
break;
|
||||
|
||||
case '}':
|
||||
if (currentCat == null)
|
||||
{
|
||||
throw new RuntimeException(String.format("Config file corrupt, attepted to close to many categories '%s:%d'", fileName, lineNum));
|
||||
}
|
||||
currentCat = currentCat.parent;
|
||||
break;
|
||||
|
||||
case '=':
|
||||
name = line.substring(nameStart, nameEnd + 1);
|
||||
|
||||
if (currentCat == null)
|
||||
{
|
||||
throw new RuntimeException(String.format("'%s' has no scope in '%s:%d'", name, fileName, lineNum));
|
||||
}
|
||||
|
||||
Property prop = new Property(name, line.substring(i + 1), type, true);
|
||||
i = line.length();
|
||||
|
||||
currentCat.put(name, prop);
|
||||
|
||||
break;
|
||||
|
||||
case ':':
|
||||
type = Property.Type.tryParse(line.substring(nameStart, nameEnd + 1).charAt(0));
|
||||
nameStart = nameEnd = -1;
|
||||
break;
|
||||
|
||||
case '<':
|
||||
if (tmpList != null)
|
||||
{
|
||||
throw new RuntimeException(String.format("Malformed list property \"%s:%d\"", fileName, lineNum));
|
||||
}
|
||||
|
||||
name = line.substring(nameStart, nameEnd + 1);
|
||||
|
||||
if (currentCat == null)
|
||||
{
|
||||
throw new RuntimeException(String.format("'%s' has no scope in '%s:%d'", name, fileName, lineNum));
|
||||
}
|
||||
|
||||
tmpList = new ArrayList<String>();
|
||||
|
||||
skip = true;
|
||||
|
||||
break;
|
||||
|
||||
case '>':
|
||||
if (tmpList == null)
|
||||
{
|
||||
throw new RuntimeException(String.format("Malformed list property \"%s:%d\"", fileName, lineNum));
|
||||
}
|
||||
|
||||
currentCat.put(name, new Property(name, tmpList.toArray(new String[tmpList.size()]), type));
|
||||
name = null;
|
||||
tmpList = null;
|
||||
type = null;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new RuntimeException(String.format("Unknown character '%s' in '%s:%d'", line.charAt(i), fileName, lineNum));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (quoted)
|
||||
{
|
||||
throw new RuntimeException(String.format("Unmatched quote in '%s:%d'", fileName, lineNum));
|
||||
}
|
||||
else if (tmpList != null && !skip)
|
||||
{
|
||||
tmpList.add(line.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (buffer != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
buffer.close();
|
||||
} catch (IOException e){}
|
||||
}
|
||||
if (input != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
input.close();
|
||||
} catch (IOException e){}
|
||||
}
|
||||
}
|
||||
|
||||
resetChangedState();
|
||||
}
|
||||
|
||||
public void save()
|
||||
{
|
||||
if (PARENT != null && PARENT != this)
|
||||
{
|
||||
PARENT.save();
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (file.getParentFile() != null)
|
||||
{
|
||||
file.getParentFile().mkdirs();
|
||||
}
|
||||
|
||||
if (!file.exists() && !file.createNewFile())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (file.canWrite())
|
||||
{
|
||||
FileOutputStream fos = new FileOutputStream(file);
|
||||
BufferedWriter buffer = new BufferedWriter(new OutputStreamWriter(fos, defaultEncoding));
|
||||
|
||||
buffer.write("# Configuration file" + NEW_LINE + NEW_LINE);
|
||||
|
||||
if (children.isEmpty())
|
||||
{
|
||||
save(buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (Map.Entry<String, Configuration> entry : children.entrySet())
|
||||
{
|
||||
buffer.write("START: \"" + entry.getKey() + "\"" + NEW_LINE);
|
||||
entry.getValue().save(buffer);
|
||||
buffer.write("END: \"" + entry.getKey() + "\"" + NEW_LINE + NEW_LINE);
|
||||
}
|
||||
}
|
||||
|
||||
buffer.close();
|
||||
fos.close();
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void save(BufferedWriter out) throws IOException
|
||||
{
|
||||
for (ConfigCategory cat : categories.values())
|
||||
{
|
||||
if (!cat.isChild())
|
||||
{
|
||||
cat.write(out, 0);
|
||||
out.newLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ConfigCategory getCategory(String category)
|
||||
{
|
||||
ConfigCategory ret = categories.get(category);
|
||||
|
||||
if (ret == null)
|
||||
{
|
||||
if (category.contains(CATEGORY_SPLITTER))
|
||||
{
|
||||
String[] hierarchy = category.split("\\"+CATEGORY_SPLITTER);
|
||||
ConfigCategory parent = categories.get(hierarchy[0]);
|
||||
|
||||
if (parent == null)
|
||||
{
|
||||
parent = new ConfigCategory(hierarchy[0]);
|
||||
categories.put(parent.getQualifiedName(), parent);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
for (int i = 1; i < hierarchy.length; i++)
|
||||
{
|
||||
String name = ConfigCategory.getQualifiedName(hierarchy[i], parent);
|
||||
ConfigCategory child = categories.get(name);
|
||||
|
||||
if (child == null)
|
||||
{
|
||||
child = new ConfigCategory(hierarchy[i], parent);
|
||||
categories.put(name, child);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
ret = child;
|
||||
parent = child;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = new ConfigCategory(category);
|
||||
categories.put(category, ret);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void removeCategory(ConfigCategory category)
|
||||
{
|
||||
for (ConfigCategory child : category.getChildren())
|
||||
{
|
||||
removeCategory(child);
|
||||
}
|
||||
|
||||
if (categories.containsKey(category.getQualifiedName()))
|
||||
{
|
||||
categories.remove(category.getQualifiedName());
|
||||
if (category.parent != null)
|
||||
{
|
||||
category.parent.removeChild(category);
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void addCustomCategoryComment(String category, String comment)
|
||||
{
|
||||
if (!caseSensitiveCustomCategories)
|
||||
category = category.toLowerCase(Locale.ENGLISH);
|
||||
getCategory(category).setComment(comment);
|
||||
}
|
||||
|
||||
private void setChild(String name, Configuration child)
|
||||
{
|
||||
if (!children.containsKey(name))
|
||||
{
|
||||
children.put(name, child);
|
||||
changed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Configuration old = children.get(name);
|
||||
child.categories = old.categories;
|
||||
child.fileName = old.fileName;
|
||||
old.changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static void enableGlobalConfig()
|
||||
{
|
||||
PARENT = new Configuration(new File(Loader.instance().getConfigDir(), "global.cfg"));
|
||||
PARENT.load();
|
||||
}
|
||||
|
||||
public static class UnicodeInputStreamReader extends Reader
|
||||
{
|
||||
private final InputStreamReader input;
|
||||
private final String defaultEnc;
|
||||
|
||||
public UnicodeInputStreamReader(InputStream source, String encoding) throws IOException
|
||||
{
|
||||
defaultEnc = encoding;
|
||||
String enc = encoding;
|
||||
byte[] data = new byte[4];
|
||||
|
||||
PushbackInputStream pbStream = new PushbackInputStream(source, data.length);
|
||||
int read = pbStream.read(data, 0, data.length);
|
||||
int size = 0;
|
||||
|
||||
int bom16 = (data[0] & 0xFF) << 8 | (data[1] & 0xFF);
|
||||
int bom24 = bom16 << 8 | (data[2] & 0xFF);
|
||||
int bom32 = bom24 << 8 | (data[3] & 0xFF);
|
||||
|
||||
if (bom24 == 0xEFBBBF)
|
||||
{
|
||||
enc = "UTF-8";
|
||||
size = 3;
|
||||
}
|
||||
else if (bom16 == 0xFEFF)
|
||||
{
|
||||
enc = "UTF-16BE";
|
||||
size = 2;
|
||||
}
|
||||
else if (bom16 == 0xFFFE)
|
||||
{
|
||||
enc = "UTF-16LE";
|
||||
size = 2;
|
||||
}
|
||||
else if (bom32 == 0x0000FEFF)
|
||||
{
|
||||
enc = "UTF-32BE";
|
||||
size = 4;
|
||||
}
|
||||
else if (bom32 == 0xFFFE0000) //This will never happen as it'll be caught by UTF-16LE,
|
||||
{ //but if anyone ever runs across a 32LE file, i'd like to disect it.
|
||||
enc = "UTF-32LE";
|
||||
size = 4;
|
||||
}
|
||||
|
||||
if (size < read)
|
||||
{
|
||||
pbStream.unread(data, size, read - size);
|
||||
}
|
||||
|
||||
this.input = new InputStreamReader(pbStream, enc);
|
||||
}
|
||||
|
||||
public String getEncoding()
|
||||
{
|
||||
return input.getEncoding();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(char[] cbuf, int off, int len) throws IOException
|
||||
{
|
||||
return input.read(cbuf, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException
|
||||
{
|
||||
input.close();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasChanged()
|
||||
{
|
||||
if (changed) return true;
|
||||
|
||||
for (ConfigCategory cat : categories.values())
|
||||
{
|
||||
if (cat.hasChanged()) return true;
|
||||
}
|
||||
|
||||
for (Configuration child : children.values())
|
||||
{
|
||||
if (child.hasChanged()) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void resetChangedState()
|
||||
{
|
||||
changed = false;
|
||||
for (ConfigCategory cat : categories.values())
|
||||
{
|
||||
cat.resetChangedState();
|
||||
}
|
||||
|
||||
for (Configuration child : children.values())
|
||||
{
|
||||
child.resetChangedState();
|
||||
}
|
||||
}
|
||||
|
||||
public Set<String> getCategoryNames()
|
||||
{
|
||||
return ImmutableSet.copyOf(categories.keySet());
|
||||
}
|
||||
}
|
||||
@@ -1,430 +0,0 @@
|
||||
package net.minecraftforge.common;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.BitSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.Hashtable;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.HashMultiset;
|
||||
import com.google.common.collect.ImmutableListMultimap;
|
||||
import com.google.common.collect.ListMultimap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.MapMaker;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Multiset;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import cpw.mods.fml.common.FMLCommonHandler;
|
||||
import cpw.mods.fml.common.FMLLog;
|
||||
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.world.ChunkCoordIntPair;
|
||||
import net.minecraft.world.MinecraftException;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.WorldManager;
|
||||
import net.minecraft.world.WorldProvider;
|
||||
import net.minecraft.world.WorldProviderEnd;
|
||||
import net.minecraft.world.WorldProviderHell;
|
||||
import net.minecraft.world.WorldProviderSurface;
|
||||
import net.minecraft.world.WorldServer;
|
||||
import net.minecraft.world.WorldServerMulti;
|
||||
import net.minecraft.world.WorldSettings;
|
||||
import net.minecraft.world.storage.ISaveHandler;
|
||||
import net.minecraft.world.storage.SaveHandler;
|
||||
import net.minecraftforge.event.world.WorldEvent;
|
||||
|
||||
public class DimensionManager
|
||||
{
|
||||
private static Hashtable<Integer, Class<? extends WorldProvider>> providers = new Hashtable<Integer, Class<? extends WorldProvider>>();
|
||||
private static Hashtable<Integer, Boolean> spawnSettings = new Hashtable<Integer, Boolean>();
|
||||
private static Hashtable<Integer, WorldServer> worlds = new Hashtable<Integer, WorldServer>();
|
||||
private static boolean hasInit = false;
|
||||
private static Hashtable<Integer, Integer> dimensions = new Hashtable<Integer, Integer>();
|
||||
private static ArrayList<Integer> unloadQueue = new ArrayList<Integer>();
|
||||
private static BitSet dimensionMap = new BitSet(Long.SIZE << 4);
|
||||
private static ConcurrentMap<World, World> weakWorldMap = new MapMaker().weakKeys().weakValues().<World,World>makeMap();
|
||||
private static Multiset<Integer> leakedWorlds = HashMultiset.create();
|
||||
|
||||
public static boolean registerProviderType(int id, Class<? extends WorldProvider> provider, boolean keepLoaded)
|
||||
{
|
||||
if (providers.containsKey(id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
providers.put(id, provider);
|
||||
spawnSettings.put(id, keepLoaded);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a Provider type, and returns a array of all dimensions that are
|
||||
* registered to this provider type.
|
||||
* If the return size is greater then 0, it is required that the caller either
|
||||
* change those dimensions's registered type, or replace this type before the
|
||||
* world is attempted to load, else the loader will throw an exception.
|
||||
*
|
||||
* @param id The provider type ID to unreigster
|
||||
* @return An array containing all dimension IDs still registered to this provider type.
|
||||
*/
|
||||
public static int[] unregisterProviderType(int id)
|
||||
{
|
||||
if (!providers.containsKey(id))
|
||||
{
|
||||
return new int[0];
|
||||
}
|
||||
providers.remove(id);
|
||||
spawnSettings.remove(id);
|
||||
|
||||
int[] ret = new int[dimensions.size()];
|
||||
int x = 0;
|
||||
for (Map.Entry<Integer, Integer> ent : dimensions.entrySet())
|
||||
{
|
||||
if (ent.getValue() == id)
|
||||
{
|
||||
ret[x++] = ent.getKey();
|
||||
}
|
||||
}
|
||||
|
||||
return Arrays.copyOf(ret, x);
|
||||
}
|
||||
|
||||
public static void init()
|
||||
{
|
||||
if (hasInit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
hasInit = true;
|
||||
|
||||
registerProviderType( 0, WorldProviderSurface.class, true);
|
||||
registerProviderType(-1, WorldProviderHell.class, true);
|
||||
registerProviderType( 1, WorldProviderEnd.class, false);
|
||||
registerDimension( 0, 0);
|
||||
registerDimension(-1, -1);
|
||||
registerDimension( 1, 1);
|
||||
}
|
||||
|
||||
public static void registerDimension(int id, int providerType)
|
||||
{
|
||||
if (!providers.containsKey(providerType))
|
||||
{
|
||||
throw new IllegalArgumentException(String.format("Failed to register dimension for id %d, provider type %d does not exist", id, providerType));
|
||||
}
|
||||
if (dimensions.containsKey(id))
|
||||
{
|
||||
throw new IllegalArgumentException(String.format("Failed to register dimension for id %d, One is already registered", id));
|
||||
}
|
||||
dimensions.put(id, providerType);
|
||||
if (id >= 0)
|
||||
{
|
||||
dimensionMap.set(id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For unregistering a dimension when the save is changed (disconnected from a server or loaded a new save
|
||||
*/
|
||||
public static void unregisterDimension(int id)
|
||||
{
|
||||
if (!dimensions.containsKey(id))
|
||||
{
|
||||
throw new IllegalArgumentException(String.format("Failed to unregister dimension for id %d; No provider registered", id));
|
||||
}
|
||||
dimensions.remove(id);
|
||||
}
|
||||
|
||||
public static boolean isDimensionRegistered(int dim)
|
||||
{
|
||||
return dimensions.containsKey(dim);
|
||||
}
|
||||
|
||||
public static int getProviderType(int dim)
|
||||
{
|
||||
if (!dimensions.containsKey(dim))
|
||||
{
|
||||
throw new IllegalArgumentException(String.format("Could not get provider type for dimension %d, does not exist", dim));
|
||||
}
|
||||
return dimensions.get(dim);
|
||||
}
|
||||
|
||||
public static WorldProvider getProvider(int dim)
|
||||
{
|
||||
return getWorld(dim).provider;
|
||||
}
|
||||
|
||||
public static Integer[] getIDs(boolean check)
|
||||
{
|
||||
if (check)
|
||||
{
|
||||
List<World> allWorlds = Lists.newArrayList(weakWorldMap.keySet());
|
||||
allWorlds.removeAll(worlds.values());
|
||||
for (ListIterator<World> li = allWorlds.listIterator(); li.hasNext(); )
|
||||
{
|
||||
World w = li.next();
|
||||
leakedWorlds.add(System.identityHashCode(w));
|
||||
}
|
||||
for (World w : allWorlds)
|
||||
{
|
||||
int leakCount = leakedWorlds.count(System.identityHashCode(w));
|
||||
if (leakCount == 5)
|
||||
{
|
||||
FMLLog.fine("The world %x (%s) may have leaked: first encounter (5 occurences).\n", System.identityHashCode(w), w.getWorldInfo().getWorldName());
|
||||
}
|
||||
else if (leakCount % 5 == 0)
|
||||
{
|
||||
FMLLog.fine("The world %x (%s) may have leaked: seen %d times.\n", System.identityHashCode(w), w.getWorldInfo().getWorldName(), leakCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
return getIDs();
|
||||
}
|
||||
public static Integer[] getIDs()
|
||||
{
|
||||
return worlds.keySet().toArray(new Integer[worlds.size()]); //Only loaded dims, since usually used to cycle through loaded worlds
|
||||
}
|
||||
|
||||
public static void setWorld(int id, WorldServer world)
|
||||
{
|
||||
if (world != null) {
|
||||
worlds.put(id, world);
|
||||
weakWorldMap.put(world, world);
|
||||
MinecraftServer.getServer().worldTickTimes.put(id, new long[100]);
|
||||
FMLLog.info("Loading dimension %d (%s) (%s)", id, world.getWorldInfo().getWorldName(), world.getMinecraftServer());
|
||||
} else {
|
||||
worlds.remove(id);
|
||||
MinecraftServer.getServer().worldTickTimes.remove(id);
|
||||
FMLLog.info("Unloading dimension %d", id);
|
||||
}
|
||||
|
||||
ArrayList<WorldServer> tmp = new ArrayList<WorldServer>();
|
||||
if (worlds.get( 0) != null)
|
||||
tmp.add(worlds.get( 0));
|
||||
if (worlds.get(-1) != null)
|
||||
tmp.add(worlds.get(-1));
|
||||
if (worlds.get( 1) != null)
|
||||
tmp.add(worlds.get( 1));
|
||||
|
||||
for (Entry<Integer, WorldServer> entry : worlds.entrySet())
|
||||
{
|
||||
int dim = entry.getKey();
|
||||
if (dim >= -1 && dim <= 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
tmp.add(entry.getValue());
|
||||
}
|
||||
|
||||
MinecraftServer.getServer().worldServers = tmp.toArray(new WorldServer[tmp.size()]);
|
||||
}
|
||||
|
||||
public static void initDimension(int dim) {
|
||||
WorldServer overworld = getWorld(0);
|
||||
if (overworld == null) {
|
||||
throw new RuntimeException("Cannot Hotload Dim: Overworld is not Loaded!");
|
||||
}
|
||||
try {
|
||||
DimensionManager.getProviderType(dim);
|
||||
} catch (Exception e) {
|
||||
System.err.println("Cannot Hotload Dim: " + e.getMessage());
|
||||
return; //If a provider hasn't been registered then we can't hotload the dim
|
||||
}
|
||||
MinecraftServer mcServer = overworld.getMinecraftServer();
|
||||
ISaveHandler savehandler = overworld.getSaveHandler();
|
||||
WorldSettings worldSettings = new WorldSettings(overworld.getWorldInfo());
|
||||
|
||||
WorldServer world = (dim == 0 ? overworld : new WorldServerMulti(mcServer, savehandler, overworld.getWorldInfo().getWorldName(), dim, worldSettings, overworld, mcServer.theProfiler, overworld.getWorldLogAgent()));
|
||||
world.addWorldAccess(new WorldManager(mcServer, world));
|
||||
MinecraftForge.EVENT_BUS.post(new WorldEvent.Load(world));
|
||||
if (!mcServer.isSinglePlayer())
|
||||
{
|
||||
world.getWorldInfo().setGameType(mcServer.getGameType());
|
||||
}
|
||||
|
||||
mcServer.setDifficultyForAllWorlds(mcServer.getDifficulty());
|
||||
}
|
||||
|
||||
public static WorldServer getWorld(int id)
|
||||
{
|
||||
return worlds.get(id);
|
||||
}
|
||||
|
||||
public static WorldServer[] getWorlds()
|
||||
{
|
||||
return worlds.values().toArray(new WorldServer[worlds.size()]);
|
||||
}
|
||||
|
||||
public static boolean shouldLoadSpawn(int dim)
|
||||
{
|
||||
int id = getProviderType(dim);
|
||||
return spawnSettings.containsKey(id) && spawnSettings.get(id);
|
||||
}
|
||||
|
||||
static
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Not public API: used internally to get dimensions that should load at
|
||||
* server startup
|
||||
*/
|
||||
public static Integer[] getStaticDimensionIDs()
|
||||
{
|
||||
return dimensions.keySet().toArray(new Integer[dimensions.keySet().size()]);
|
||||
}
|
||||
public static WorldProvider createProviderFor(int dim)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (dimensions.containsKey(dim))
|
||||
{
|
||||
WorldProvider provider = providers.get(getProviderType(dim)).newInstance();
|
||||
provider.setDimension(dim);
|
||||
return provider;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new RuntimeException(String.format("No WorldProvider bound for dimension %d", dim)); //It's going to crash anyway at this point. Might as well be informative
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
FMLCommonHandler.instance().getFMLLogger().log(Level.SEVERE,String.format("An error occured trying to create an instance of WorldProvider %d (%s)",
|
||||
dim, providers.get(getProviderType(dim)).getSimpleName()),e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void unloadWorld(int id) {
|
||||
unloadQueue.add(id);
|
||||
}
|
||||
|
||||
/*
|
||||
* To be called by the server at the appropriate time, do not call from mod code.
|
||||
*/
|
||||
public static void unloadWorlds(Hashtable<Integer, long[]> worldTickTimes) {
|
||||
for (int id : unloadQueue) {
|
||||
WorldServer w = worlds.get(id);
|
||||
try {
|
||||
if (w != null)
|
||||
{
|
||||
w.saveAllChunks(true, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
FMLLog.warning("Unexpected world unload - world %d is already unloaded", id);
|
||||
}
|
||||
} catch (MinecraftException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (w != null)
|
||||
{
|
||||
MinecraftForge.EVENT_BUS.post(new WorldEvent.Unload(w));
|
||||
w.flush();
|
||||
setWorld(id, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
unloadQueue.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the next free dimension ID. Note: you are not guaranteed a contiguous
|
||||
* block of free ids. Always call for each individual ID you wish to get.
|
||||
* @return the next free dimension ID
|
||||
*/
|
||||
public static int getNextFreeDimId() {
|
||||
int next = 0;
|
||||
while (true)
|
||||
{
|
||||
next = dimensionMap.nextClearBit(next);
|
||||
if (dimensions.containsKey(next))
|
||||
{
|
||||
dimensionMap.set(next);
|
||||
}
|
||||
else
|
||||
{
|
||||
return next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static NBTTagCompound saveDimensionDataMap()
|
||||
{
|
||||
int[] data = new int[(dimensionMap.length() + Integer.SIZE - 1 )/ Integer.SIZE];
|
||||
NBTTagCompound dimMap = new NBTTagCompound();
|
||||
for (int i = 0; i < data.length; i++)
|
||||
{
|
||||
int val = 0;
|
||||
for (int j = 0; j < Integer.SIZE; j++)
|
||||
{
|
||||
val |= dimensionMap.get(i * Integer.SIZE + j) ? (1 << j) : 0;
|
||||
}
|
||||
data[i] = val;
|
||||
}
|
||||
dimMap.setIntArray("DimensionArray", data);
|
||||
return dimMap;
|
||||
}
|
||||
|
||||
public static void loadDimensionDataMap(NBTTagCompound compoundTag)
|
||||
{
|
||||
if (compoundTag == null)
|
||||
{
|
||||
dimensionMap.clear();
|
||||
for (Integer id : dimensions.keySet())
|
||||
{
|
||||
if (id >= 0)
|
||||
{
|
||||
dimensionMap.set(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int[] intArray = compoundTag.getIntArray("DimensionArray");
|
||||
for (int i = 0; i < intArray.length; i++)
|
||||
{
|
||||
for (int j = 0; j < Integer.SIZE; j++)
|
||||
{
|
||||
dimensionMap.set(i * Integer.SIZE + j, (intArray[i] & (1 << j)) != 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current root directory for the world save. Accesses getSaveHandler from the overworld
|
||||
* @return the root directory of the save
|
||||
*/
|
||||
public static File getCurrentSaveRootDirectory()
|
||||
{
|
||||
if (DimensionManager.getWorld(0) != null)
|
||||
{
|
||||
return ((SaveHandler)DimensionManager.getWorld(0).getSaveHandler()).getWorldDirectory();
|
||||
}
|
||||
else if (MinecraftServer.getServer() != null)
|
||||
{
|
||||
MinecraftServer srv = MinecraftServer.getServer();
|
||||
SaveHandler saveHandler = (SaveHandler) srv.getActiveAnvilConverter().getSaveLoader(srv.getFolderName(), false);
|
||||
return saveHandler.getWorldDirectory();
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
package net.minecraftforge.common;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Random;
|
||||
|
||||
import cpw.mods.fml.common.FMLLog;
|
||||
|
||||
import net.minecraft.inventory.IInventory;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.WeightedRandom;
|
||||
import net.minecraft.util.WeightedRandomChestContent;
|
||||
import net.minecraft.util.WeightedRandomItem;
|
||||
|
||||
import static net.minecraftforge.common.ChestGenHooks.DUNGEON_CHEST;
|
||||
|
||||
public class DungeonHooks
|
||||
{
|
||||
private static ArrayList<DungeonMob> dungeonMobs = new ArrayList<DungeonMob>();
|
||||
|
||||
/**
|
||||
* Adds a mob to the possible list of creatures the spawner will create.
|
||||
* If the mob is already in the spawn list, the rarity will be added to the existing one,
|
||||
* causing the mob to be more common.
|
||||
*
|
||||
* @param name The name of the monster, use the same name used when registering the entity.
|
||||
* @param rarity The rarity of selecting this mob over others. Must be greater then 0.
|
||||
* Vanilla Minecraft has the following mobs:
|
||||
* Spider 100
|
||||
* Skeleton 100
|
||||
* Zombie 200
|
||||
* Meaning, Zombies are twice as common as spiders or skeletons.
|
||||
* @return The new rarity of the monster,
|
||||
*/
|
||||
public static float addDungeonMob(String name, int rarity)
|
||||
{
|
||||
if (rarity <= 0)
|
||||
{
|
||||
throw new IllegalArgumentException("Rarity must be greater then zero");
|
||||
}
|
||||
|
||||
for (DungeonMob mob : dungeonMobs)
|
||||
{
|
||||
if (name.equals(mob.type))
|
||||
{
|
||||
return mob.itemWeight += rarity;
|
||||
}
|
||||
}
|
||||
|
||||
dungeonMobs.add(new DungeonMob(rarity, name));
|
||||
return rarity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will completely remove a Mob from the dungeon spawn list.
|
||||
*
|
||||
* @param name The name of the mob to remove
|
||||
* @return The rarity of the removed mob, prior to being removed.
|
||||
*/
|
||||
public static int removeDungeonMob(String name)
|
||||
{
|
||||
for (DungeonMob mob : dungeonMobs)
|
||||
{
|
||||
if (name.equals(mob.type))
|
||||
{
|
||||
dungeonMobs.remove(mob);
|
||||
return mob.itemWeight;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a random mob name from the list.
|
||||
* @param rand World generation random number generator
|
||||
* @return The mob name
|
||||
*/
|
||||
public static String getRandomDungeonMob(Random rand)
|
||||
{
|
||||
DungeonMob mob = (DungeonMob)WeightedRandom.getRandomItem(rand, dungeonMobs);
|
||||
if (mob == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
return mob.type;
|
||||
}
|
||||
|
||||
|
||||
public static class DungeonMob extends WeightedRandomItem
|
||||
{
|
||||
public String type;
|
||||
public DungeonMob(int weight, String type)
|
||||
{
|
||||
super(weight);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object target)
|
||||
{
|
||||
return target instanceof DungeonMob && type.equals(((DungeonMob)target).type);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
{
|
||||
addDungeonMob("Skeleton", 100);
|
||||
addDungeonMob("Zombie", 200);
|
||||
addDungeonMob("Spider", 100);
|
||||
}
|
||||
}
|
||||
@@ -1,286 +0,0 @@
|
||||
package net.minecraftforge.common;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
|
||||
import cpw.mods.fml.common.FMLCommonHandler;
|
||||
import cpw.mods.fml.common.FMLLog;
|
||||
|
||||
import net.minecraft.block.EnumMobType;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.enchantment.EnumEnchantmentType;
|
||||
import net.minecraft.entity.EnumCreatureAttribute;
|
||||
import net.minecraft.entity.EnumCreatureType;
|
||||
import net.minecraft.entity.EnumEntitySize;
|
||||
import net.minecraft.entity.player.EnumStatus;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.item.EnumArmorMaterial;
|
||||
import net.minecraft.item.EnumToolMaterial;
|
||||
import net.minecraft.util.EnumArt;
|
||||
import net.minecraft.util.EnumMovingObjectType;
|
||||
import net.minecraft.world.EnumSkyBlock;
|
||||
import net.minecraft.world.gen.structure.EnumDoor;
|
||||
import net.minecraftforge.classloading.FMLForgePlugin;
|
||||
|
||||
public class EnumHelper
|
||||
{
|
||||
private static Object reflectionFactory = null;
|
||||
private static Method newConstructorAccessor = null;
|
||||
private static Method newInstance = null;
|
||||
private static Method newFieldAccessor = null;
|
||||
private static Method fieldAccessorSet = null;
|
||||
private static boolean isSetup = false;
|
||||
|
||||
//Some enums are decompiled with extra arguments, so lets check for that
|
||||
private static Class[][] commonTypes =
|
||||
{
|
||||
{EnumAction.class},
|
||||
{EnumArmorMaterial.class, int.class, int[].class, int.class},
|
||||
{EnumArt.class, String.class, int.class, int.class, int.class, int.class},
|
||||
{EnumCreatureAttribute.class},
|
||||
{EnumCreatureType.class, Class.class, int.class, Material.class, boolean.class},
|
||||
{EnumDoor.class},
|
||||
{EnumEnchantmentType.class},
|
||||
{EnumEntitySize.class},
|
||||
{EnumMobType.class},
|
||||
{EnumMovingObjectType.class},
|
||||
{EnumSkyBlock.class, int.class},
|
||||
{EnumStatus.class},
|
||||
{EnumToolMaterial.class, int.class, int.class, float.class, int.class, int.class}
|
||||
};
|
||||
|
||||
public static EnumAction addAction(String name)
|
||||
{
|
||||
return addEnum(EnumAction.class, name);
|
||||
}
|
||||
public static EnumArmorMaterial addArmorMaterial(String name, int durability, int[] reductionAmounts, int enchantability)
|
||||
{
|
||||
return addEnum(EnumArmorMaterial.class, name, durability, reductionAmounts, enchantability);
|
||||
}
|
||||
public static EnumArt addArt(String name, String tile, int sizeX, int sizeY, int offsetX, int offsetY)
|
||||
{
|
||||
return addEnum(EnumArt.class, name, tile, sizeX, sizeY, offsetX, offsetY);
|
||||
}
|
||||
public static EnumCreatureAttribute addCreatureAttribute(String name)
|
||||
{
|
||||
return addEnum(EnumCreatureAttribute.class, name);
|
||||
}
|
||||
public static EnumCreatureType addCreatureType(String name, Class typeClass, int maxNumber, Material material, boolean peaceful)
|
||||
{
|
||||
return addEnum(EnumCreatureType.class, name, typeClass, maxNumber, material, peaceful);
|
||||
}
|
||||
public static EnumDoor addDoor(String name)
|
||||
{
|
||||
return addEnum(EnumDoor.class, name);
|
||||
}
|
||||
public static EnumEnchantmentType addEnchantmentType(String name)
|
||||
{
|
||||
return addEnum(EnumEnchantmentType.class, name);
|
||||
}
|
||||
public static EnumEntitySize addEntitySize(String name)
|
||||
{
|
||||
return addEnum(EnumEntitySize.class, name);
|
||||
}
|
||||
public static EnumMobType addMobType(String name)
|
||||
{
|
||||
return addEnum(EnumMobType.class, name);
|
||||
}
|
||||
public static EnumMovingObjectType addMovingObjectType(String name)
|
||||
{
|
||||
if (!isSetup)
|
||||
{
|
||||
setup();
|
||||
}
|
||||
|
||||
return addEnum(EnumMovingObjectType.class, name);
|
||||
}
|
||||
public static EnumSkyBlock addSkyBlock(String name, int lightValue)
|
||||
{
|
||||
return addEnum(EnumSkyBlock.class, name, lightValue);
|
||||
}
|
||||
public static EnumStatus addStatus(String name)
|
||||
{
|
||||
return addEnum(EnumStatus.class, name);
|
||||
}
|
||||
public static EnumToolMaterial addToolMaterial(String name, int harvestLevel, int maxUses, float efficiency, int damage, int enchantability)
|
||||
{
|
||||
return addEnum(EnumToolMaterial.class, name, harvestLevel, maxUses, efficiency, damage, enchantability);
|
||||
}
|
||||
|
||||
private static void setup()
|
||||
{
|
||||
if (isSetup)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Method getReflectionFactory = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("getReflectionFactory");
|
||||
reflectionFactory = getReflectionFactory.invoke(null);
|
||||
newConstructorAccessor = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("newConstructorAccessor", Constructor.class);
|
||||
newInstance = Class.forName("sun.reflect.ConstructorAccessor").getDeclaredMethod("newInstance", Object[].class);
|
||||
newFieldAccessor = Class.forName("sun.reflect.ReflectionFactory").getDeclaredMethod("newFieldAccessor", Field.class, boolean.class);
|
||||
fieldAccessorSet = Class.forName("sun.reflect.FieldAccessor").getDeclaredMethod("set", Object.class, Object.class);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
isSetup = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Everything below this is found at the site below, and updated to be able to compile in Eclipse/Java 1.6+
|
||||
* Also modified for use in decompiled code.
|
||||
* Found at: http://niceideas.ch/roller2/badtrash/entry/java_create_enum_instances_dynamically
|
||||
*/
|
||||
private static Object getConstructorAccessor(Class<?> enumClass, Class<?>[] additionalParameterTypes) throws Exception
|
||||
{
|
||||
Class<?>[] parameterTypes = new Class[additionalParameterTypes.length + 2];
|
||||
parameterTypes[0] = String.class;
|
||||
parameterTypes[1] = int.class;
|
||||
System.arraycopy(additionalParameterTypes, 0, parameterTypes, 2, additionalParameterTypes.length);
|
||||
return newConstructorAccessor.invoke(reflectionFactory, enumClass.getDeclaredConstructor(parameterTypes));
|
||||
}
|
||||
|
||||
private static < T extends Enum<? >> T makeEnum(Class<T> enumClass, String value, int ordinal, Class<?>[] additionalTypes, Object[] additionalValues) throws Exception
|
||||
{
|
||||
Object[] parms = new Object[additionalValues.length + 2];
|
||||
parms[0] = value;
|
||||
parms[1] = Integer.valueOf(ordinal);
|
||||
System.arraycopy(additionalValues, 0, parms, 2, additionalValues.length);
|
||||
return enumClass.cast(newInstance.invoke(getConstructorAccessor(enumClass, additionalTypes), new Object[] {parms}));
|
||||
}
|
||||
|
||||
public static void setFailsafeFieldValue(Field field, Object target, Object value) throws Exception
|
||||
{
|
||||
field.setAccessible(true);
|
||||
Field modifiersField = Field.class.getDeclaredField("modifiers");
|
||||
modifiersField.setAccessible(true);
|
||||
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
|
||||
Object fieldAccessor = newFieldAccessor.invoke(reflectionFactory, field, false);
|
||||
fieldAccessorSet.invoke(fieldAccessor, target, value);
|
||||
}
|
||||
|
||||
private static void blankField(Class<?> enumClass, String fieldName) throws Exception
|
||||
{
|
||||
for (Field field : Class.class.getDeclaredFields())
|
||||
{
|
||||
if (field.getName().contains(fieldName))
|
||||
{
|
||||
field.setAccessible(true);
|
||||
setFailsafeFieldValue(field, enumClass, null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void cleanEnumCache(Class<?> enumClass) throws Exception
|
||||
{
|
||||
blankField(enumClass, "enumConstantDirectory");
|
||||
blankField(enumClass, "enumConstants");
|
||||
}
|
||||
|
||||
public static <T extends Enum<? >> T addEnum(Class<T> enumType, String enumName, Object... paramValues)
|
||||
{
|
||||
return addEnum(commonTypes, enumType, enumName, paramValues);
|
||||
}
|
||||
|
||||
public static <T extends Enum<? >> T addEnum(Class[][] map, Class<T> enumType, String enumName, Object... paramValues)
|
||||
{
|
||||
for (Class[] lookup : map)
|
||||
{
|
||||
if (lookup[0] == enumType)
|
||||
{
|
||||
Class<?>[] paramTypes = new Class<?>[lookup.length - 1];
|
||||
if (paramTypes.length > 0)
|
||||
{
|
||||
System.arraycopy(lookup, 1, paramTypes, 0, paramTypes.length);
|
||||
}
|
||||
return addEnum(enumType, enumName, paramTypes, paramValues);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends Enum<? >> T addEnum(Class<T> enumType, String enumName, Class<?>[] paramTypes, Object[] paramValues)
|
||||
{
|
||||
if (!isSetup)
|
||||
{
|
||||
setup();
|
||||
}
|
||||
|
||||
Field valuesField = null;
|
||||
Field[] fields = enumType.getDeclaredFields();
|
||||
|
||||
for (Field field : fields)
|
||||
{
|
||||
String name = field.getName();
|
||||
if (name.equals("$VALUES") || name.equals("ENUM$VALUES")) //Added 'ENUM$VALUES' because Eclipse's internal compiler doesn't follow standards
|
||||
{
|
||||
valuesField = field;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int flags = (FMLForgePlugin.RUNTIME_DEOBF ? Modifier.PUBLIC : Modifier.PRIVATE) | Modifier.STATIC | Modifier.FINAL | 0x1000 /*SYNTHETIC*/;
|
||||
if (valuesField == null)
|
||||
{
|
||||
String valueType = String.format("[L%s;", enumType.getName().replace('.', '/'));
|
||||
|
||||
for (Field field : fields)
|
||||
{
|
||||
if ((field.getModifiers() & flags) == flags &&
|
||||
field.getType().getName().replace('.', '/').equals(valueType)) //Apparently some JVMs return .'s and some don't..
|
||||
{
|
||||
valuesField = field;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (valuesField == null)
|
||||
{
|
||||
FMLLog.severe("Could not find $VALUES field for enum: %s", enumType.getName());
|
||||
FMLLog.severe("Runtime Deobf: %s", FMLForgePlugin.RUNTIME_DEOBF);
|
||||
FMLLog.severe("Flags: %s", String.format("%16s", Integer.toBinaryString(flags)).replace(' ', '0'));
|
||||
FMLLog.severe("Fields:");
|
||||
for (Field field : fields)
|
||||
{
|
||||
String mods = String.format("%16s", Integer.toBinaryString(field.getModifiers())).replace(' ', '0');
|
||||
FMLLog.severe(" %s %s: %s", mods, field.getName(), field.getType().getName());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
valuesField.setAccessible(true);
|
||||
|
||||
try
|
||||
{
|
||||
T[] previousValues = (T[])valuesField.get(enumType);
|
||||
List<T> values = new ArrayList<T>(Arrays.asList(previousValues));
|
||||
T newValue = (T)makeEnum(enumType, enumName, values.size(), paramTypes, paramValues);
|
||||
values.add(newValue);
|
||||
setFailsafeFieldValue(valuesField, null, values.toArray((T[]) Array.newInstance(enumType, 0)));
|
||||
cleanEnumCache(enumType);
|
||||
|
||||
return newValue;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
{
|
||||
if (!isSetup)
|
||||
{
|
||||
setup();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package net.minecraftforge.common;
|
||||
|
||||
public enum EnumPlantType
|
||||
{
|
||||
Plains,
|
||||
Desert,
|
||||
Beach,
|
||||
Cave,
|
||||
Water,
|
||||
Nether,
|
||||
Crop
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package net.minecraftforge.common;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.util.ChatMessageComponent;
|
||||
import net.minecraft.util.ChunkCoordinates;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
//Preliminary, simple Fake Player class
|
||||
public class FakePlayer extends EntityPlayer
|
||||
{
|
||||
public FakePlayer(World world, String name)
|
||||
{
|
||||
super(world, name);
|
||||
}
|
||||
|
||||
public void sendChatToPlayer(String s){}
|
||||
public boolean canCommandSenderUseCommand(int i, String s){ return false; }
|
||||
public ChunkCoordinates getPlayerCoordinates()
|
||||
{
|
||||
return new ChunkCoordinates(0,0,0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void func_110122_a(ChatMessageComponent chatmessagecomponent){}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package net.minecraftforge.common;
|
||||
|
||||
import net.minecraft.world.World;
|
||||
|
||||
//To be expanded for generic Mod fake players?
|
||||
public class FakePlayerFactory
|
||||
{
|
||||
private static FakePlayer MINECRAFT_PLAYER = null;
|
||||
|
||||
public static FakePlayer getMinecraft(World world)
|
||||
{
|
||||
if (MINECRAFT_PLAYER == null)
|
||||
{
|
||||
MINECRAFT_PLAYER = new FakePlayer(world, "[Minecraft]");
|
||||
}
|
||||
return MINECRAFT_PLAYER;
|
||||
}
|
||||
}
|
||||
@@ -1,999 +0,0 @@
|
||||
package net.minecraftforge.common;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.BiMap;
|
||||
import com.google.common.collect.ForwardingSet;
|
||||
import com.google.common.collect.HashBiMap;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableListMultimap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSetMultimap;
|
||||
import com.google.common.collect.LinkedHashMultimap;
|
||||
import com.google.common.collect.ListMultimap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.MapMaker;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.Multimaps;
|
||||
import com.google.common.collect.Multiset;
|
||||
import com.google.common.collect.SetMultimap;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.collect.TreeMultiset;
|
||||
|
||||
import cpw.mods.fml.common.FMLLog;
|
||||
import cpw.mods.fml.common.Loader;
|
||||
import cpw.mods.fml.common.ModContainer;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
import net.minecraft.world.ChunkCoordIntPair;
|
||||
import net.minecraft.nbt.CompressedStreamTools;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.util.MathHelper;
|
||||
import net.minecraft.nbt.NBTBase;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.WorldServer;
|
||||
import net.minecraftforge.common.ForgeChunkManager.Ticket;
|
||||
import net.minecraftforge.event.Event;
|
||||
|
||||
/**
|
||||
* Manages chunkloading for mods.
|
||||
*
|
||||
* The basic principle is a ticket based system.
|
||||
* 1. Mods register a callback {@link #setForcedChunkLoadingCallback(Object, LoadingCallback)}
|
||||
* 2. Mods ask for a ticket {@link #requestTicket(Object, World, Type)} and then hold on to that ticket.
|
||||
* 3. Mods request chunks to stay loaded {@link #forceChunk(Ticket, ChunkCoordIntPair)} or remove chunks from force loading {@link #unforceChunk(Ticket, ChunkCoordIntPair)}.
|
||||
* 4. When a world unloads, the tickets associated with that world are saved by the chunk manager.
|
||||
* 5. When a world loads, saved tickets are offered to the mods associated with the tickets. The {@link Ticket#getModData()} that is set by the mod should be used to re-register
|
||||
* chunks to stay loaded (and maybe take other actions).
|
||||
*
|
||||
* The chunkloading is configurable at runtime. The file "config/forgeChunkLoading.cfg" contains both default configuration for chunkloading, and a sample individual mod
|
||||
* specific override section.
|
||||
*
|
||||
* @author cpw
|
||||
*
|
||||
*/
|
||||
public class ForgeChunkManager
|
||||
{
|
||||
private static int defaultMaxCount;
|
||||
private static int defaultMaxChunks;
|
||||
private static boolean overridesEnabled;
|
||||
|
||||
private static Map<World, Multimap<String, Ticket>> tickets = new MapMaker().weakKeys().makeMap();
|
||||
private static Map<String, Integer> ticketConstraints = Maps.newHashMap();
|
||||
private static Map<String, Integer> chunkConstraints = Maps.newHashMap();
|
||||
|
||||
private static SetMultimap<String, Ticket> playerTickets = HashMultimap.create();
|
||||
|
||||
private static Map<String, LoadingCallback> callbacks = Maps.newHashMap();
|
||||
|
||||
private static Map<World, ImmutableSetMultimap<ChunkCoordIntPair,Ticket>> forcedChunks = new MapMaker().weakKeys().makeMap();
|
||||
private static BiMap<UUID,Ticket> pendingEntities = HashBiMap.create();
|
||||
|
||||
private static Map<World,Cache<Long, Chunk>> dormantChunkCache = new MapMaker().weakKeys().makeMap();
|
||||
|
||||
private static File cfgFile;
|
||||
private static Configuration config;
|
||||
private static int playerTicketLength;
|
||||
private static int dormantChunkCacheSize;
|
||||
|
||||
private static Set<String> warnedMods = Sets.newHashSet();
|
||||
/**
|
||||
* All mods requiring chunkloading need to implement this to handle the
|
||||
* re-registration of chunk tickets at world loading time
|
||||
*
|
||||
* @author cpw
|
||||
*
|
||||
*/
|
||||
public interface LoadingCallback
|
||||
{
|
||||
/**
|
||||
* Called back when tickets are loaded from the world to allow the
|
||||
* mod to re-register the chunks associated with those tickets. The list supplied
|
||||
* here is truncated to length prior to use. Tickets unwanted by the
|
||||
* mod must be disposed of manually unless the mod is an OrderedLoadingCallback instance
|
||||
* in which case, they will have been disposed of by the earlier callback.
|
||||
*
|
||||
* @param tickets The tickets to re-register. The list is immutable and cannot be manipulated directly. Copy it first.
|
||||
* @param world the world
|
||||
*/
|
||||
public void ticketsLoaded(List<Ticket> tickets, World world);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a special LoadingCallback that can be implemented as well as the
|
||||
* LoadingCallback to provide access to additional behaviour.
|
||||
* Specifically, this callback will fire prior to Forge dropping excess
|
||||
* tickets. Tickets in the returned list are presumed ordered and excess will
|
||||
* be truncated from the returned list.
|
||||
* This allows the mod to control not only if they actually <em>want</em> a ticket but
|
||||
* also their preferred ticket ordering.
|
||||
*
|
||||
* @author cpw
|
||||
*
|
||||
*/
|
||||
public interface OrderedLoadingCallback extends LoadingCallback
|
||||
{
|
||||
/**
|
||||
* Called back when tickets are loaded from the world to allow the
|
||||
* mod to decide if it wants the ticket still, and prioritise overflow
|
||||
* based on the ticket count.
|
||||
* WARNING: You cannot force chunks in this callback, it is strictly for allowing the mod
|
||||
* to be more selective in which tickets it wishes to preserve in an overflow situation
|
||||
*
|
||||
* @param tickets The tickets that you will want to select from. The list is immutable and cannot be manipulated directly. Copy it first.
|
||||
* @param world The world
|
||||
* @param maxTicketCount The maximum number of tickets that will be allowed.
|
||||
* @return A list of the tickets this mod wishes to continue using. This list will be truncated
|
||||
* to "maxTicketCount" size after the call returns and then offered to the other callback
|
||||
* method
|
||||
*/
|
||||
public List<Ticket> ticketsLoaded(List<Ticket> tickets, World world, int maxTicketCount);
|
||||
}
|
||||
|
||||
public interface PlayerOrderedLoadingCallback extends LoadingCallback
|
||||
{
|
||||
/**
|
||||
* Called back when tickets are loaded from the world to allow the
|
||||
* mod to decide if it wants the ticket still.
|
||||
* This is for player bound tickets rather than mod bound tickets. It is here so mods can
|
||||
* decide they want to dump all player tickets
|
||||
*
|
||||
* WARNING: You cannot force chunks in this callback, it is strictly for allowing the mod
|
||||
* to be more selective in which tickets it wishes to preserve
|
||||
*
|
||||
* @param tickets The tickets that you will want to select from. The list is immutable and cannot be manipulated directly. Copy it first.
|
||||
* @param world The world
|
||||
* @return A list of the tickets this mod wishes to use. This list will subsequently be offered
|
||||
* to the main callback for action
|
||||
*/
|
||||
public ListMultimap<String, Ticket> playerTicketsLoaded(ListMultimap<String, Ticket> tickets, World world);
|
||||
}
|
||||
public enum Type
|
||||
{
|
||||
|
||||
/**
|
||||
* For non-entity registrations
|
||||
*/
|
||||
NORMAL,
|
||||
/**
|
||||
* For entity registrations
|
||||
*/
|
||||
ENTITY
|
||||
}
|
||||
public static class Ticket
|
||||
{
|
||||
private String modId;
|
||||
private Type ticketType;
|
||||
private LinkedHashSet<ChunkCoordIntPair> requestedChunks;
|
||||
private NBTTagCompound modData;
|
||||
public final World world;
|
||||
private int maxDepth;
|
||||
private String entityClazz;
|
||||
private int entityChunkX;
|
||||
private int entityChunkZ;
|
||||
private Entity entity;
|
||||
private String player;
|
||||
|
||||
Ticket(String modId, Type type, World world)
|
||||
{
|
||||
this.modId = modId;
|
||||
this.ticketType = type;
|
||||
this.world = world;
|
||||
this.maxDepth = getMaxChunkDepthFor(modId);
|
||||
this.requestedChunks = Sets.newLinkedHashSet();
|
||||
}
|
||||
|
||||
Ticket(String modId, Type type, World world, String player)
|
||||
{
|
||||
this(modId, type, world);
|
||||
if (player != null)
|
||||
{
|
||||
this.player = player;
|
||||
}
|
||||
else
|
||||
{
|
||||
FMLLog.log(Level.SEVERE, "Attempt to create a player ticket without a valid player");
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* The chunk list depth can be manipulated up to the maximal grant allowed for the mod. This value is configurable. Once the maximum is reached,
|
||||
* the least recently forced chunk, by original registration time, is removed from the forced chunk list.
|
||||
*
|
||||
* @param depth The new depth to set
|
||||
*/
|
||||
public void setChunkListDepth(int depth)
|
||||
{
|
||||
if (depth > getMaxChunkDepthFor(modId) || (depth <= 0 && getMaxChunkDepthFor(modId) > 0))
|
||||
{
|
||||
FMLLog.warning("The mod %s tried to modify the chunk ticket depth to: %d, its allowed maximum is: %d", modId, depth, getMaxChunkDepthFor(modId));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.maxDepth = depth;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current max depth for this ticket.
|
||||
* Should be the same as getMaxChunkListDepth()
|
||||
* unless setChunkListDepth has been called.
|
||||
*
|
||||
* @return Current max depth
|
||||
*/
|
||||
public int getChunkListDepth()
|
||||
{
|
||||
return maxDepth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum chunk depth size
|
||||
*
|
||||
* @return The maximum chunk depth size
|
||||
*/
|
||||
public int getMaxChunkListDepth()
|
||||
{
|
||||
return getMaxChunkDepthFor(modId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind the entity to the ticket for {@link Type#ENTITY} type tickets. Other types will throw a runtime exception.
|
||||
*
|
||||
* @param entity The entity to bind
|
||||
*/
|
||||
public void bindEntity(Entity entity)
|
||||
{
|
||||
if (ticketType!=Type.ENTITY)
|
||||
{
|
||||
throw new RuntimeException("Cannot bind an entity to a non-entity ticket");
|
||||
}
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the {@link NBTTagCompound} that stores mod specific data for the chunk ticket.
|
||||
* Example data to store would be a TileEntity or Block location. This is persisted with the ticket and
|
||||
* provided to the {@link LoadingCallback} for the mod. It is recommended to use this to recover
|
||||
* useful state information for the forced chunks.
|
||||
*
|
||||
* @return The custom compound tag for mods to store additional chunkloading data
|
||||
*/
|
||||
public NBTTagCompound getModData()
|
||||
{
|
||||
if (this.modData == null)
|
||||
{
|
||||
this.modData = new NBTTagCompound();
|
||||
}
|
||||
return modData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the entity associated with this {@link Type#ENTITY} type ticket
|
||||
* @return the entity
|
||||
*/
|
||||
public Entity getEntity()
|
||||
{
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this a player associated ticket rather than a mod associated ticket?
|
||||
*/
|
||||
public boolean isPlayerTicket()
|
||||
{
|
||||
return player != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the player associated with this ticket
|
||||
*/
|
||||
public String getPlayerName()
|
||||
{
|
||||
return player;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the associated mod id
|
||||
*/
|
||||
public String getModId()
|
||||
{
|
||||
return modId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ticket type
|
||||
*/
|
||||
public Type getType()
|
||||
{
|
||||
return ticketType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of requested chunks for this ticket.
|
||||
*/
|
||||
public ImmutableSet getChunkList()
|
||||
{
|
||||
return ImmutableSet.copyOf(requestedChunks);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ForceChunkEvent extends Event {
|
||||
public final Ticket ticket;
|
||||
public final ChunkCoordIntPair location;
|
||||
|
||||
public ForceChunkEvent(Ticket ticket, ChunkCoordIntPair location)
|
||||
{
|
||||
this.ticket = ticket;
|
||||
this.location = location;
|
||||
}
|
||||
}
|
||||
|
||||
public static class UnforceChunkEvent extends Event {
|
||||
public final Ticket ticket;
|
||||
public final ChunkCoordIntPair location;
|
||||
|
||||
public UnforceChunkEvent(Ticket ticket, ChunkCoordIntPair location)
|
||||
{
|
||||
this.ticket = ticket;
|
||||
this.location = location;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allows dynamically loading world mods to test if there are chunk tickets in the world
|
||||
* Mods that add dynamically generated worlds (like Mystcraft) should call this method
|
||||
* to determine if the world should be loaded during server starting.
|
||||
*
|
||||
* @param chunkDir The chunk directory to test: should be equivalent to {@link WorldServer#getChunkSaveLocation()}
|
||||
* @return if there are tickets outstanding for this world or not
|
||||
*/
|
||||
public static boolean savedWorldHasForcedChunkTickets(File chunkDir)
|
||||
{
|
||||
File chunkLoaderData = new File(chunkDir, "forcedchunks.dat");
|
||||
|
||||
if (chunkLoaderData.exists() && chunkLoaderData.isFile())
|
||||
{
|
||||
;
|
||||
try
|
||||
{
|
||||
NBTTagCompound forcedChunkData = CompressedStreamTools.read(chunkLoaderData);
|
||||
return forcedChunkData.getTagList("TicketList").tagCount() > 0;
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void loadWorld(World world)
|
||||
{
|
||||
ArrayListMultimap<String, Ticket> newTickets = ArrayListMultimap.<String, Ticket>create();
|
||||
tickets.put(world, newTickets);
|
||||
|
||||
forcedChunks.put(world, ImmutableSetMultimap.<ChunkCoordIntPair,Ticket>of());
|
||||
|
||||
if (!(world instanceof WorldServer))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
dormantChunkCache.put(world, CacheBuilder.newBuilder().maximumSize(dormantChunkCacheSize).<Long, Chunk>build());
|
||||
WorldServer worldServer = (WorldServer) world;
|
||||
File chunkDir = worldServer.getChunkSaveLocation();
|
||||
File chunkLoaderData = new File(chunkDir, "forcedchunks.dat");
|
||||
|
||||
if (chunkLoaderData.exists() && chunkLoaderData.isFile())
|
||||
{
|
||||
ArrayListMultimap<String, Ticket> loadedTickets = ArrayListMultimap.<String, Ticket>create();
|
||||
Map<String,ListMultimap<String,Ticket>> playerLoadedTickets = Maps.newHashMap();
|
||||
NBTTagCompound forcedChunkData;
|
||||
try
|
||||
{
|
||||
forcedChunkData = CompressedStreamTools.read(chunkLoaderData);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
FMLLog.log(Level.WARNING, e, "Unable to read forced chunk data at %s - it will be ignored", chunkLoaderData.getAbsolutePath());
|
||||
return;
|
||||
}
|
||||
NBTTagList ticketList = forcedChunkData.getTagList("TicketList");
|
||||
for (int i = 0; i < ticketList.tagCount(); i++)
|
||||
{
|
||||
NBTTagCompound ticketHolder = (NBTTagCompound) ticketList.tagAt(i);
|
||||
String modId = ticketHolder.getString("Owner");
|
||||
boolean isPlayer = "Forge".equals(modId);
|
||||
|
||||
if (!isPlayer && !Loader.isModLoaded(modId))
|
||||
{
|
||||
FMLLog.warning("Found chunkloading data for mod %s which is currently not available or active - it will be removed from the world save", modId);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isPlayer && !callbacks.containsKey(modId))
|
||||
{
|
||||
FMLLog.warning("The mod %s has registered persistent chunkloading data but doesn't seem to want to be called back with it - it will be removed from the world save", modId);
|
||||
continue;
|
||||
}
|
||||
|
||||
NBTTagList tickets = ticketHolder.getTagList("Tickets");
|
||||
for (int j = 0; j < tickets.tagCount(); j++)
|
||||
{
|
||||
NBTTagCompound ticket = (NBTTagCompound) tickets.tagAt(j);
|
||||
modId = ticket.hasKey("ModId") ? ticket.getString("ModId") : modId;
|
||||
Type type = Type.values()[ticket.getByte("Type")];
|
||||
byte ticketChunkDepth = ticket.getByte("ChunkListDepth");
|
||||
Ticket tick = new Ticket(modId, type, world);
|
||||
if (ticket.hasKey("ModData"))
|
||||
{
|
||||
tick.modData = ticket.getCompoundTag("ModData");
|
||||
}
|
||||
if (ticket.hasKey("Player"))
|
||||
{
|
||||
tick.player = ticket.getString("Player");
|
||||
if (!playerLoadedTickets.containsKey(tick.modId))
|
||||
{
|
||||
playerLoadedTickets.put(modId, ArrayListMultimap.<String,Ticket>create());
|
||||
}
|
||||
playerLoadedTickets.get(tick.modId).put(tick.player, tick);
|
||||
}
|
||||
else
|
||||
{
|
||||
loadedTickets.put(modId, tick);
|
||||
}
|
||||
if (type == Type.ENTITY)
|
||||
{
|
||||
tick.entityChunkX = ticket.getInteger("chunkX");
|
||||
tick.entityChunkZ = ticket.getInteger("chunkZ");
|
||||
UUID uuid = new UUID(ticket.getLong("PersistentIDMSB"), ticket.getLong("PersistentIDLSB"));
|
||||
// add the ticket to the "pending entity" list
|
||||
pendingEntities.put(uuid, tick);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Ticket tick : ImmutableSet.copyOf(pendingEntities.values()))
|
||||
{
|
||||
if (tick.ticketType == Type.ENTITY && tick.entity == null)
|
||||
{
|
||||
// force the world to load the entity's chunk
|
||||
// the load will come back through the loadEntity method and attach the entity
|
||||
// to the ticket
|
||||
world.getChunkFromChunkCoords(tick.entityChunkX, tick.entityChunkZ);
|
||||
}
|
||||
}
|
||||
for (Ticket tick : ImmutableSet.copyOf(pendingEntities.values()))
|
||||
{
|
||||
if (tick.ticketType == Type.ENTITY && tick.entity == null)
|
||||
{
|
||||
FMLLog.warning("Failed to load persistent chunkloading entity %s from store.", pendingEntities.inverse().get(tick));
|
||||
loadedTickets.remove(tick.modId, tick);
|
||||
}
|
||||
}
|
||||
pendingEntities.clear();
|
||||
// send callbacks
|
||||
for (String modId : loadedTickets.keySet())
|
||||
{
|
||||
LoadingCallback loadingCallback = callbacks.get(modId);
|
||||
if (loadingCallback == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int maxTicketLength = getMaxTicketLengthFor(modId);
|
||||
List<Ticket> tickets = loadedTickets.get(modId);
|
||||
if (loadingCallback instanceof OrderedLoadingCallback)
|
||||
{
|
||||
OrderedLoadingCallback orderedLoadingCallback = (OrderedLoadingCallback) loadingCallback;
|
||||
tickets = orderedLoadingCallback.ticketsLoaded(ImmutableList.copyOf(tickets), world, maxTicketLength);
|
||||
}
|
||||
if (tickets.size() > maxTicketLength)
|
||||
{
|
||||
FMLLog.warning("The mod %s has too many open chunkloading tickets %d. Excess will be dropped", modId, tickets.size());
|
||||
tickets.subList(maxTicketLength, tickets.size()).clear();
|
||||
}
|
||||
ForgeChunkManager.tickets.get(world).putAll(modId, tickets);
|
||||
loadingCallback.ticketsLoaded(ImmutableList.copyOf(tickets), world);
|
||||
}
|
||||
for (String modId : playerLoadedTickets.keySet())
|
||||
{
|
||||
LoadingCallback loadingCallback = callbacks.get(modId);
|
||||
if (loadingCallback == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
ListMultimap<String,Ticket> tickets = playerLoadedTickets.get(modId);
|
||||
if (loadingCallback instanceof PlayerOrderedLoadingCallback)
|
||||
{
|
||||
PlayerOrderedLoadingCallback orderedLoadingCallback = (PlayerOrderedLoadingCallback) loadingCallback;
|
||||
tickets = orderedLoadingCallback.playerTicketsLoaded(ImmutableListMultimap.copyOf(tickets), world);
|
||||
playerTickets.putAll(tickets);
|
||||
}
|
||||
ForgeChunkManager.tickets.get(world).putAll("Forge", tickets.values());
|
||||
loadingCallback.ticketsLoaded(ImmutableList.copyOf(tickets.values()), world);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void unloadWorld(World world)
|
||||
{
|
||||
// World save fires before this event so the chunk loading info will be done
|
||||
if (!(world instanceof WorldServer))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
forcedChunks.remove(world);
|
||||
dormantChunkCache.remove(world);
|
||||
// integrated server is shutting down
|
||||
if (!MinecraftServer.getServer().isServerRunning())
|
||||
{
|
||||
playerTickets.clear();
|
||||
tickets.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a chunkloading callback for the supplied mod object
|
||||
*
|
||||
* @param mod The mod instance registering the callback
|
||||
* @param callback The code to call back when forced chunks are loaded
|
||||
*/
|
||||
public static void setForcedChunkLoadingCallback(Object mod, LoadingCallback callback)
|
||||
{
|
||||
ModContainer container = getContainer(mod);
|
||||
if (container == null)
|
||||
{
|
||||
FMLLog.warning("Unable to register a callback for an unknown mod %s (%s : %x)", mod, mod.getClass().getName(), System.identityHashCode(mod));
|
||||
return;
|
||||
}
|
||||
|
||||
callbacks.put(container.getModId(), callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Discover the available tickets for the mod in the world
|
||||
*
|
||||
* @param mod The mod that will own the tickets
|
||||
* @param world The world
|
||||
* @return The count of tickets left for the mod in the supplied world
|
||||
*/
|
||||
public static int ticketCountAvailableFor(Object mod, World world)
|
||||
{
|
||||
ModContainer container = getContainer(mod);
|
||||
if (container!=null)
|
||||
{
|
||||
String modId = container.getModId();
|
||||
int allowedCount = getMaxTicketLengthFor(modId);
|
||||
return allowedCount - tickets.get(world).get(modId).size();
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static ModContainer getContainer(Object mod)
|
||||
{
|
||||
ModContainer container = Loader.instance().getModObjectList().inverse().get(mod);
|
||||
return container;
|
||||
}
|
||||
|
||||
public static int getMaxTicketLengthFor(String modId)
|
||||
{
|
||||
int allowedCount = ticketConstraints.containsKey(modId) && overridesEnabled ? ticketConstraints.get(modId) : defaultMaxCount;
|
||||
return allowedCount;
|
||||
}
|
||||
|
||||
public static int getMaxChunkDepthFor(String modId)
|
||||
{
|
||||
int allowedCount = chunkConstraints.containsKey(modId) && overridesEnabled ? chunkConstraints.get(modId) : defaultMaxChunks;
|
||||
return allowedCount;
|
||||
}
|
||||
|
||||
public static int ticketCountAvailableFor(String username)
|
||||
{
|
||||
return playerTicketLength - playerTickets.get(username).size();
|
||||
}
|
||||
|
||||
public static Ticket requestPlayerTicket(Object mod, String player, World world, Type type)
|
||||
{
|
||||
ModContainer mc = getContainer(mod);
|
||||
if (mc == null)
|
||||
{
|
||||
FMLLog.log(Level.SEVERE, "Failed to locate the container for mod instance %s (%s : %x)", mod, mod.getClass().getName(), System.identityHashCode(mod));
|
||||
return null;
|
||||
}
|
||||
if (playerTickets.get(player).size()>playerTicketLength)
|
||||
{
|
||||
FMLLog.warning("Unable to assign further chunkloading tickets to player %s (on behalf of mod %s)", player, mc.getModId());
|
||||
return null;
|
||||
}
|
||||
Ticket ticket = new Ticket(mc.getModId(),type,world,player);
|
||||
playerTickets.put(player, ticket);
|
||||
tickets.get(world).put("Forge", ticket);
|
||||
return ticket;
|
||||
}
|
||||
/**
|
||||
* Request a chunkloading ticket of the appropriate type for the supplied mod
|
||||
*
|
||||
* @param mod The mod requesting a ticket
|
||||
* @param world The world in which it is requesting the ticket
|
||||
* @param type The type of ticket
|
||||
* @return A ticket with which to register chunks for loading, or null if no further tickets are available
|
||||
*/
|
||||
public static Ticket requestTicket(Object mod, World world, Type type)
|
||||
{
|
||||
ModContainer container = getContainer(mod);
|
||||
if (container == null)
|
||||
{
|
||||
FMLLog.log(Level.SEVERE, "Failed to locate the container for mod instance %s (%s : %x)", mod, mod.getClass().getName(), System.identityHashCode(mod));
|
||||
return null;
|
||||
}
|
||||
String modId = container.getModId();
|
||||
if (!callbacks.containsKey(modId))
|
||||
{
|
||||
FMLLog.severe("The mod %s has attempted to request a ticket without a listener in place", modId);
|
||||
throw new RuntimeException("Invalid ticket request");
|
||||
}
|
||||
|
||||
int allowedCount = ticketConstraints.containsKey(modId) ? ticketConstraints.get(modId) : defaultMaxCount;
|
||||
|
||||
if (tickets.get(world).get(modId).size() >= allowedCount)
|
||||
{
|
||||
if (!warnedMods.contains(modId))
|
||||
{
|
||||
FMLLog.info("The mod %s has attempted to allocate a chunkloading ticket beyond it's currently allocated maximum : %d", modId, allowedCount);
|
||||
warnedMods.add(modId);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
Ticket ticket = new Ticket(modId, type, world);
|
||||
tickets.get(world).put(modId, ticket);
|
||||
|
||||
return ticket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the ticket back to the system. This will also unforce any chunks held by the ticket so that they can be unloaded and/or stop ticking.
|
||||
*
|
||||
* @param ticket The ticket to release
|
||||
*/
|
||||
public static void releaseTicket(Ticket ticket)
|
||||
{
|
||||
if (ticket == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (ticket.isPlayerTicket() ? !playerTickets.containsValue(ticket) : !tickets.get(ticket.world).containsEntry(ticket.modId, ticket))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (ticket.requestedChunks!=null)
|
||||
{
|
||||
for (ChunkCoordIntPair chunk : ImmutableSet.copyOf(ticket.requestedChunks))
|
||||
{
|
||||
unforceChunk(ticket, chunk);
|
||||
}
|
||||
}
|
||||
if (ticket.isPlayerTicket())
|
||||
{
|
||||
playerTickets.remove(ticket.player, ticket);
|
||||
tickets.get(ticket.world).remove("Forge",ticket);
|
||||
}
|
||||
else
|
||||
{
|
||||
tickets.get(ticket.world).remove(ticket.modId, ticket);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Force the supplied chunk coordinate to be loaded by the supplied ticket. If the ticket's {@link Ticket#maxDepth} is exceeded, the least
|
||||
* recently registered chunk is unforced and may be unloaded.
|
||||
* It is safe to force the chunk several times for a ticket, it will not generate duplication or change the ordering.
|
||||
*
|
||||
* @param ticket The ticket registering the chunk
|
||||
* @param chunk The chunk to force
|
||||
*/
|
||||
public static void forceChunk(Ticket ticket, ChunkCoordIntPair chunk)
|
||||
{
|
||||
if (ticket == null || chunk == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (ticket.ticketType == Type.ENTITY && ticket.entity == null)
|
||||
{
|
||||
throw new RuntimeException("Attempted to use an entity ticket to force a chunk, without an entity");
|
||||
}
|
||||
if (ticket.isPlayerTicket() ? !playerTickets.containsValue(ticket) : !tickets.get(ticket.world).containsEntry(ticket.modId, ticket))
|
||||
{
|
||||
FMLLog.severe("The mod %s attempted to force load a chunk with an invalid ticket. This is not permitted.", ticket.modId);
|
||||
return;
|
||||
}
|
||||
ticket.requestedChunks.add(chunk);
|
||||
MinecraftForge.EVENT_BUS.post(new ForceChunkEvent(ticket, chunk));
|
||||
|
||||
ImmutableSetMultimap<ChunkCoordIntPair, Ticket> newMap = ImmutableSetMultimap.<ChunkCoordIntPair,Ticket>builder().putAll(forcedChunks.get(ticket.world)).put(chunk, ticket).build();
|
||||
forcedChunks.put(ticket.world, newMap);
|
||||
if (ticket.maxDepth > 0 && ticket.requestedChunks.size() > ticket.maxDepth)
|
||||
{
|
||||
ChunkCoordIntPair removed = ticket.requestedChunks.iterator().next();
|
||||
unforceChunk(ticket,removed);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reorganize the internal chunk list so that the chunk supplied is at the *end* of the list
|
||||
* This helps if you wish to guarantee a certain "automatic unload ordering" for the chunks
|
||||
* in the ticket list
|
||||
*
|
||||
* @param ticket The ticket holding the chunk list
|
||||
* @param chunk The chunk you wish to push to the end (so that it would be unloaded last)
|
||||
*/
|
||||
public static void reorderChunk(Ticket ticket, ChunkCoordIntPair chunk)
|
||||
{
|
||||
if (ticket == null || chunk == null || !ticket.requestedChunks.contains(chunk))
|
||||
{
|
||||
return;
|
||||
}
|
||||
ticket.requestedChunks.remove(chunk);
|
||||
ticket.requestedChunks.add(chunk);
|
||||
}
|
||||
/**
|
||||
* Unforce the supplied chunk, allowing it to be unloaded and stop ticking.
|
||||
*
|
||||
* @param ticket The ticket holding the chunk
|
||||
* @param chunk The chunk to unforce
|
||||
*/
|
||||
public static void unforceChunk(Ticket ticket, ChunkCoordIntPair chunk)
|
||||
{
|
||||
if (ticket == null || chunk == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
ticket.requestedChunks.remove(chunk);
|
||||
MinecraftForge.EVENT_BUS.post(new UnforceChunkEvent(ticket, chunk));
|
||||
LinkedHashMultimap<ChunkCoordIntPair, Ticket> copy = LinkedHashMultimap.create(forcedChunks.get(ticket.world));
|
||||
copy.remove(chunk, ticket);
|
||||
ImmutableSetMultimap<ChunkCoordIntPair, Ticket> newMap = ImmutableSetMultimap.copyOf(copy);
|
||||
forcedChunks.put(ticket.world,newMap);
|
||||
}
|
||||
|
||||
static void loadConfiguration()
|
||||
{
|
||||
for (String mod : config.getCategoryNames())
|
||||
{
|
||||
if (mod.equals("Forge") || mod.equals("defaults"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Property modTC = config.get(mod, "maximumTicketCount", 200);
|
||||
Property modCPT = config.get(mod, "maximumChunksPerTicket", 25);
|
||||
ticketConstraints.put(mod, modTC.getInt(200));
|
||||
chunkConstraints.put(mod, modCPT.getInt(25));
|
||||
}
|
||||
if (config.hasChanged())
|
||||
{
|
||||
config.save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of persistent chunks in the world. This set is immutable.
|
||||
* @param world
|
||||
* @return the list of persistent chunks in the world
|
||||
*/
|
||||
public static ImmutableSetMultimap<ChunkCoordIntPair, Ticket> getPersistentChunksFor(World world)
|
||||
{
|
||||
return forcedChunks.containsKey(world) ? forcedChunks.get(world) : ImmutableSetMultimap.<ChunkCoordIntPair,Ticket>of();
|
||||
}
|
||||
|
||||
static void saveWorld(World world)
|
||||
{
|
||||
// only persist persistent worlds
|
||||
if (!(world instanceof WorldServer)) { return; }
|
||||
WorldServer worldServer = (WorldServer) world;
|
||||
File chunkDir = worldServer.getChunkSaveLocation();
|
||||
File chunkLoaderData = new File(chunkDir, "forcedchunks.dat");
|
||||
|
||||
NBTTagCompound forcedChunkData = new NBTTagCompound();
|
||||
NBTTagList ticketList = new NBTTagList();
|
||||
forcedChunkData.setTag("TicketList", ticketList);
|
||||
|
||||
Multimap<String, Ticket> ticketSet = tickets.get(worldServer);
|
||||
for (String modId : ticketSet.keySet())
|
||||
{
|
||||
NBTTagCompound ticketHolder = new NBTTagCompound();
|
||||
ticketList.appendTag(ticketHolder);
|
||||
|
||||
ticketHolder.setString("Owner", modId);
|
||||
NBTTagList tickets = new NBTTagList();
|
||||
ticketHolder.setTag("Tickets", tickets);
|
||||
|
||||
for (Ticket tick : ticketSet.get(modId))
|
||||
{
|
||||
NBTTagCompound ticket = new NBTTagCompound();
|
||||
ticket.setByte("Type", (byte) tick.ticketType.ordinal());
|
||||
ticket.setByte("ChunkListDepth", (byte) tick.maxDepth);
|
||||
if (tick.isPlayerTicket())
|
||||
{
|
||||
ticket.setString("ModId", tick.modId);
|
||||
ticket.setString("Player", tick.player);
|
||||
}
|
||||
if (tick.modData != null)
|
||||
{
|
||||
ticket.setCompoundTag("ModData", tick.modData);
|
||||
}
|
||||
if (tick.ticketType == Type.ENTITY && tick.entity != null && tick.entity.addEntityID(new NBTTagCompound()))
|
||||
{
|
||||
ticket.setInteger("chunkX", MathHelper.floor_double(tick.entity.chunkCoordX));
|
||||
ticket.setInteger("chunkZ", MathHelper.floor_double(tick.entity.chunkCoordZ));
|
||||
ticket.setLong("PersistentIDMSB", tick.entity.getPersistentID().getMostSignificantBits());
|
||||
ticket.setLong("PersistentIDLSB", tick.entity.getPersistentID().getLeastSignificantBits());
|
||||
tickets.appendTag(ticket);
|
||||
}
|
||||
else if (tick.ticketType != Type.ENTITY)
|
||||
{
|
||||
tickets.appendTag(ticket);
|
||||
}
|
||||
}
|
||||
}
|
||||
try
|
||||
{
|
||||
CompressedStreamTools.write(forcedChunkData, chunkLoaderData);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
FMLLog.log(Level.WARNING, e, "Unable to write forced chunk data to %s - chunkloading won't work", chunkLoaderData.getAbsolutePath());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void loadEntity(Entity entity)
|
||||
{
|
||||
UUID id = entity.getPersistentID();
|
||||
Ticket tick = pendingEntities.get(id);
|
||||
if (tick != null)
|
||||
{
|
||||
tick.bindEntity(entity);
|
||||
pendingEntities.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
public static void putDormantChunk(long coords, Chunk chunk)
|
||||
{
|
||||
Cache<Long, Chunk> cache = dormantChunkCache.get(chunk.worldObj);
|
||||
if (cache != null)
|
||||
{
|
||||
cache.put(coords, chunk);
|
||||
}
|
||||
}
|
||||
|
||||
public static Chunk fetchDormantChunk(long coords, World world)
|
||||
{
|
||||
Cache<Long, Chunk> cache = dormantChunkCache.get(world);
|
||||
if (cache == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
Chunk chunk = cache.getIfPresent(coords);
|
||||
if (chunk != null)
|
||||
{
|
||||
for (List<Entity> eList : chunk.entityLists)
|
||||
{
|
||||
for (Entity e: eList)
|
||||
{
|
||||
e.resetEntityId();
|
||||
}
|
||||
}
|
||||
}
|
||||
return chunk;
|
||||
}
|
||||
|
||||
static void captureConfig(File configDir)
|
||||
{
|
||||
cfgFile = new File(configDir,"forgeChunkLoading.cfg");
|
||||
config = new Configuration(cfgFile, true);
|
||||
try
|
||||
{
|
||||
config.load();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
File dest = new File(cfgFile.getParentFile(),"forgeChunkLoading.cfg.bak");
|
||||
if (dest.exists())
|
||||
{
|
||||
dest.delete();
|
||||
}
|
||||
cfgFile.renameTo(dest);
|
||||
FMLLog.log(Level.SEVERE, e, "A critical error occured reading the forgeChunkLoading.cfg file, defaults will be used - the invalid file is backed up at forgeChunkLoading.cfg.bak");
|
||||
}
|
||||
config.addCustomCategoryComment("defaults", "Default configuration for forge chunk loading control");
|
||||
Property maxTicketCount = config.get("defaults", "maximumTicketCount", 200);
|
||||
maxTicketCount.comment = "The default maximum ticket count for a mod which does not have an override\n" +
|
||||
"in this file. This is the number of chunk loading requests a mod is allowed to make.";
|
||||
defaultMaxCount = maxTicketCount.getInt(200);
|
||||
|
||||
Property maxChunks = config.get("defaults", "maximumChunksPerTicket", 25);
|
||||
maxChunks.comment = "The default maximum number of chunks a mod can force, per ticket, \n" +
|
||||
"for a mod without an override. This is the maximum number of chunks a single ticket can force.";
|
||||
defaultMaxChunks = maxChunks.getInt(25);
|
||||
|
||||
Property playerTicketCount = config.get("defaults", "playerTicketCount", 500);
|
||||
playerTicketCount.comment = "The number of tickets a player can be assigned instead of a mod. This is shared across all mods and it is up to the mods to use it.";
|
||||
playerTicketLength = playerTicketCount.getInt(500);
|
||||
|
||||
Property dormantChunkCacheSizeProperty = config.get("defaults", "dormantChunkCacheSize", 0);
|
||||
dormantChunkCacheSizeProperty.comment = "Unloaded chunks can first be kept in a dormant cache for quicker\n" +
|
||||
"loading times. Specify the size (in chunks) of that cache here";
|
||||
dormantChunkCacheSize = dormantChunkCacheSizeProperty.getInt(0);
|
||||
FMLLog.info("Configured a dormant chunk cache size of %d", dormantChunkCacheSizeProperty.getInt(0));
|
||||
|
||||
Property modOverridesEnabled = config.get("defaults", "enabled", true);
|
||||
modOverridesEnabled.comment = "Are mod overrides enabled?";
|
||||
overridesEnabled = modOverridesEnabled.getBoolean(true);
|
||||
|
||||
config.addCustomCategoryComment("Forge", "Sample mod specific control section.\n" +
|
||||
"Copy this section and rename the with the modid for the mod you wish to override.\n" +
|
||||
"A value of zero in either entry effectively disables any chunkloading capabilities\n" +
|
||||
"for that mod");
|
||||
|
||||
Property sampleTC = config.get("Forge", "maximumTicketCount", 200);
|
||||
sampleTC.comment = "Maximum ticket count for the mod. Zero disables chunkloading capabilities.";
|
||||
sampleTC = config.get("Forge", "maximumChunksPerTicket", 25);
|
||||
sampleTC.comment = "Maximum chunks per ticket for the mod.";
|
||||
for (String mod : config.getCategoryNames())
|
||||
{
|
||||
if (mod.equals("Forge") || mod.equals("defaults"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Property modTC = config.get(mod, "maximumTicketCount", 200);
|
||||
Property modCPT = config.get(mod, "maximumChunksPerTicket", 25);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static ConfigCategory getConfigFor(Object mod)
|
||||
{
|
||||
ModContainer container = getContainer(mod);
|
||||
if (container != null)
|
||||
{
|
||||
return config.getCategory(container.getModId());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void addConfigProperty(Object mod, String propertyName, String value, Property.Type type)
|
||||
{
|
||||
ModContainer container = getContainer(mod);
|
||||
if (container != null)
|
||||
{
|
||||
ConfigCategory cat = config.getCategory(container.getModId());
|
||||
cat.put(propertyName, new Property(propertyName, value, type));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
package net.minecraftforge.common;
|
||||
|
||||
public enum ForgeDirection
|
||||
{
|
||||
/** -Y */
|
||||
DOWN(0, -1, 0),
|
||||
|
||||
/** +Y */
|
||||
UP(0, 1, 0),
|
||||
|
||||
/** -Z */
|
||||
NORTH(0, 0, -1),
|
||||
|
||||
/** +Z */
|
||||
SOUTH(0, 0, 1),
|
||||
|
||||
/** -X */
|
||||
WEST(-1, 0, 0),
|
||||
|
||||
/** +X */
|
||||
EAST(1, 0, 0),
|
||||
|
||||
/**
|
||||
* Used only by getOrientation, for invalid inputs
|
||||
*/
|
||||
UNKNOWN(0, 0, 0);
|
||||
|
||||
public final int offsetX;
|
||||
public final int offsetY;
|
||||
public final int offsetZ;
|
||||
public final int flag;
|
||||
public static final ForgeDirection[] VALID_DIRECTIONS = {DOWN, UP, NORTH, SOUTH, WEST, EAST};
|
||||
public static final int[] OPPOSITES = {1, 0, 3, 2, 5, 4, 6};
|
||||
// Left hand rule rotation matrix for all possible axes of rotation
|
||||
public static final int[][] ROTATION_MATRIX = {
|
||||
{0, 1, 4, 5, 3, 2, 6},
|
||||
{0, 1, 5, 4, 2, 3, 6},
|
||||
{5, 4, 2, 3, 0, 1, 6},
|
||||
{4, 5, 2, 3, 1, 0, 6},
|
||||
{2, 3, 1, 0, 4, 5, 6},
|
||||
{3, 2, 0, 1, 4, 5, 6},
|
||||
{0, 1, 2, 3, 4, 5, 6},
|
||||
};
|
||||
|
||||
private ForgeDirection(int x, int y, int z)
|
||||
{
|
||||
offsetX = x;
|
||||
offsetY = y;
|
||||
offsetZ = z;
|
||||
flag = 1 << ordinal();
|
||||
}
|
||||
|
||||
public static ForgeDirection getOrientation(int id)
|
||||
{
|
||||
if (id >= 0 && id < VALID_DIRECTIONS.length)
|
||||
{
|
||||
return VALID_DIRECTIONS[id];
|
||||
}
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
public ForgeDirection getOpposite()
|
||||
{
|
||||
return getOrientation(OPPOSITES[ordinal()]);
|
||||
}
|
||||
|
||||
public ForgeDirection getRotation(ForgeDirection axis)
|
||||
{
|
||||
return getOrientation(ROTATION_MATRIX[axis.ordinal()][ordinal()]);
|
||||
}
|
||||
}
|
||||
@@ -1,184 +0,0 @@
|
||||
package net.minecraftforge.common;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import net.minecraft.nbt.NBTBase;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.server.management.PlayerInstance;
|
||||
import net.minecraft.world.storage.SaveHandler;
|
||||
import net.minecraft.world.storage.WorldInfo;
|
||||
import net.minecraftforge.common.network.ForgeConnectionHandler;
|
||||
import net.minecraftforge.common.network.ForgeNetworkHandler;
|
||||
import net.minecraftforge.common.network.ForgePacketHandler;
|
||||
import net.minecraftforge.common.network.ForgeTinyPacketHandler;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
|
||||
import cpw.mods.fml.common.DummyModContainer;
|
||||
import cpw.mods.fml.common.FMLLog;
|
||||
import cpw.mods.fml.common.LoadController;
|
||||
import cpw.mods.fml.common.Loader;
|
||||
import cpw.mods.fml.common.ModMetadata;
|
||||
import cpw.mods.fml.common.WorldAccessContainer;
|
||||
import cpw.mods.fml.common.event.FMLConstructionEvent;
|
||||
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||
import cpw.mods.fml.common.network.FMLNetworkHandler;
|
||||
import cpw.mods.fml.common.network.NetworkMod;
|
||||
|
||||
import static net.minecraftforge.common.ForgeVersion.*;
|
||||
|
||||
@NetworkMod(
|
||||
channels = "FORGE",
|
||||
connectionHandler = ForgeConnectionHandler.class,
|
||||
packetHandler = ForgePacketHandler.class,
|
||||
tinyPacketHandler = ForgeTinyPacketHandler.class
|
||||
)
|
||||
public class ForgeDummyContainer extends DummyModContainer implements WorldAccessContainer
|
||||
{
|
||||
public static int clumpingThreshold = 64;
|
||||
public static boolean legacyFurnaceSides = false;
|
||||
public static boolean removeErroringEntities = false;
|
||||
public static boolean removeErroringTileEntities = false;
|
||||
public static boolean disableStitchedFileSaving = false;
|
||||
|
||||
public ForgeDummyContainer()
|
||||
{
|
||||
super(new ModMetadata());
|
||||
ModMetadata meta = getMetadata();
|
||||
meta.modId = "Forge";
|
||||
meta.name = "Minecraft Forge";
|
||||
meta.version = String.format("%d.%d.%d.%d", majorVersion, minorVersion, revisionVersion, buildVersion);
|
||||
meta.credits = "Made possible with help from many people";
|
||||
meta.authorList = Arrays.asList("LexManos", "Eloraam", "Spacetoad");
|
||||
meta.description = "Minecraft Forge is a common open source API allowing a broad range of mods " +
|
||||
"to work cooperatively together. It allows many mods to be created without " +
|
||||
"them editing the main Minecraft code.";
|
||||
meta.url = "http://MinecraftForge.net";
|
||||
meta.updateUrl = "http://MinecraftForge.net/forum/index.php/topic,5.0.html";
|
||||
meta.screenshots = new String[0];
|
||||
meta.logoFile = "/forge_logo.png";
|
||||
|
||||
Configuration config = null;
|
||||
File cfgFile = new File(Loader.instance().getConfigDir(), "forge.cfg");
|
||||
try
|
||||
{
|
||||
config = new Configuration(cfgFile);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
System.out.println("Error loading forge.cfg, deleting file and resetting: ");
|
||||
e.printStackTrace();
|
||||
|
||||
if (cfgFile.exists())
|
||||
cfgFile.delete();
|
||||
|
||||
config = new Configuration(cfgFile);
|
||||
}
|
||||
if (!config.isChild)
|
||||
{
|
||||
config.load();
|
||||
Property enableGlobalCfg = config.get(Configuration.CATEGORY_GENERAL, "enableGlobalConfig", false);
|
||||
if (enableGlobalCfg.getBoolean(false))
|
||||
{
|
||||
Configuration.enableGlobalConfig();
|
||||
}
|
||||
}
|
||||
Property prop;
|
||||
prop = config.get(Configuration.CATEGORY_GENERAL, "clumpingThreshold", 64);
|
||||
prop.comment = "Controls the number threshold at which Packet51 is preferred over Packet52, default and minimum 64, maximum 1024";
|
||||
clumpingThreshold = prop.getInt(64);
|
||||
if (clumpingThreshold > 1024 || clumpingThreshold < 64)
|
||||
{
|
||||
clumpingThreshold = 64;
|
||||
prop.set(64);
|
||||
}
|
||||
|
||||
prop = config.get(Configuration.CATEGORY_GENERAL, "legacyFurnaceOutput", false);
|
||||
prop.comment = "Controls the sides of vanilla furnaces for Forge's ISidedInventroy, Vanilla defines the output as the bottom, but mods/Forge define it as the sides. Settings this to true will restore the old side relations.";
|
||||
legacyFurnaceSides = prop.getBoolean(false);
|
||||
|
||||
prop = config.get(Configuration.CATEGORY_GENERAL, "removeErroringEntities", false);
|
||||
prop.comment = "Set this to just remove any TileEntity that throws a error in there update method instead of closing the server and reporting a crash log. BE WARNED THIS COULD SCREW UP EVERYTHING USE SPARINGLY WE ARE NOT RESPONSIBLE FOR DAMAGES.";
|
||||
removeErroringEntities = prop.getBoolean(false);
|
||||
|
||||
if (removeErroringEntities)
|
||||
{
|
||||
FMLLog.warning("Enableing removal of erroring Entities USE AT YOUR OWN RISK");
|
||||
}
|
||||
|
||||
prop = config.get(Configuration.CATEGORY_GENERAL, "removeErroringTileEntities", false);
|
||||
prop.comment = "Set this to just remove any TileEntity that throws a error in there update method instead of closing the server and reporting a crash log. BE WARNED THIS COULD SCREW UP EVERYTHING USE SPARINGLY WE ARE NOT RESPONSIBLE FOR DAMAGES.";
|
||||
removeErroringTileEntities = prop.getBoolean(false);
|
||||
|
||||
if (removeErroringTileEntities)
|
||||
{
|
||||
FMLLog.warning("Enableing removal of erroring Tile Entities USE AT YOUR OWN RISK");
|
||||
}
|
||||
|
||||
prop = config.get(Configuration.CATEGORY_GENERAL, "disableStitchedFileSaving", true);
|
||||
prop.comment = "Set this to just disable the texture stitcher from writing the 'debug.stitched_{name}.png file to disc. Just a small performance tweak. Default: true";
|
||||
disableStitchedFileSaving = prop.getBoolean(true);
|
||||
|
||||
if (config.hasChanged())
|
||||
{
|
||||
config.save();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean registerBus(EventBus bus, LoadController controller)
|
||||
{
|
||||
bus.register(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void modConstruction(FMLConstructionEvent evt)
|
||||
{
|
||||
FMLLog.info("Registering Forge Packet Handler");
|
||||
try
|
||||
{
|
||||
FMLNetworkHandler.instance().registerNetworkMod(new ForgeNetworkHandler(this));
|
||||
FMLLog.info("Succeeded registering Forge Packet Handler");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
FMLLog.log(Level.SEVERE, e, "Failed to register packet handler for Forge");
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void preInit(FMLPreInitializationEvent evt)
|
||||
{
|
||||
ForgeChunkManager.captureConfig(evt.getModConfigurationDirectory());
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void postInit(FMLPostInitializationEvent evt)
|
||||
{
|
||||
ForgeChunkManager.loadConfiguration();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBTTagCompound getDataForWriting(SaveHandler handler, WorldInfo info)
|
||||
{
|
||||
NBTTagCompound forgeData = new NBTTagCompound();
|
||||
NBTTagCompound dimData = DimensionManager.saveDimensionDataMap();
|
||||
forgeData.setCompoundTag("DimensionData", dimData);
|
||||
return forgeData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readData(SaveHandler handler, WorldInfo info, Map<String, NBTBase> propertyMap, NBTTagCompound tag)
|
||||
{
|
||||
if (tag.hasKey("DimensionData"))
|
||||
{
|
||||
DimensionManager.loadDimensionDataMap(tag.hasKey("DimensionData") ? tag.getCompoundTag("DimensionData") : null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,401 +0,0 @@
|
||||
package net.minecraftforge.common;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.item.EntityItem;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemArmor;
|
||||
import net.minecraft.item.ItemAxe;
|
||||
import net.minecraft.item.ItemPickaxe;
|
||||
import net.minecraft.item.ItemSpade;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.network.NetServerHandler;
|
||||
import net.minecraft.util.ChatMessageComponent;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.EnumMovingObjectType;
|
||||
import net.minecraft.util.MovingObjectPosition;
|
||||
import net.minecraft.util.WeightedRandom;
|
||||
import net.minecraft.util.WeightedRandomItem;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.event.ForgeEventFactory;
|
||||
import net.minecraftforge.event.ServerChatEvent;
|
||||
import net.minecraftforge.event.entity.item.ItemTossEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingAttackEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingDeathEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingDropsEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingEvent.LivingJumpEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingFallEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingHurtEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingSetAttackTargetEvent;
|
||||
|
||||
public class ForgeHooks
|
||||
{
|
||||
static class GrassEntry extends WeightedRandomItem
|
||||
{
|
||||
public final Block block;
|
||||
public final int metadata;
|
||||
public GrassEntry(Block block, int meta, int weight)
|
||||
{
|
||||
super(weight);
|
||||
this.block = block;
|
||||
this.metadata = meta;
|
||||
}
|
||||
}
|
||||
|
||||
static class SeedEntry extends WeightedRandomItem
|
||||
{
|
||||
public final ItemStack seed;
|
||||
public SeedEntry(ItemStack seed, int weight)
|
||||
{
|
||||
super(weight);
|
||||
this.seed = seed;
|
||||
}
|
||||
}
|
||||
static final List<GrassEntry> grassList = new ArrayList<GrassEntry>();
|
||||
static final List<SeedEntry> seedList = new ArrayList<SeedEntry>();
|
||||
|
||||
public static void plantGrass(World world, int x, int y, int z)
|
||||
{
|
||||
GrassEntry grass = (GrassEntry)WeightedRandom.getRandomItem(world.rand, grassList);
|
||||
if (grass == null || grass.block == null || !grass.block.canBlockStay(world, x, y, z))
|
||||
{
|
||||
return;
|
||||
}
|
||||
world.setBlock(x, y, z, grass.block.blockID, grass.metadata, 3);
|
||||
}
|
||||
|
||||
public static ItemStack getGrassSeed(World world)
|
||||
{
|
||||
SeedEntry entry = (SeedEntry)WeightedRandom.getRandomItem(world.rand, seedList);
|
||||
if (entry == null || entry.seed == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return entry.seed.copy();
|
||||
}
|
||||
|
||||
private static boolean toolInit = false;
|
||||
static HashMap<Item, List> toolClasses = new HashMap<Item, List>();
|
||||
static HashMap<List, Integer> toolHarvestLevels = new HashMap<List, Integer>();
|
||||
static HashSet<List> toolEffectiveness = new HashSet<List>();
|
||||
|
||||
public static boolean canHarvestBlock(Block block, EntityPlayer player, int metadata)
|
||||
{
|
||||
if (block.blockMaterial.isToolNotRequired())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
ItemStack stack = player.inventory.getCurrentItem();
|
||||
if (stack == null)
|
||||
{
|
||||
return player.canHarvestBlock(block);
|
||||
}
|
||||
|
||||
List info = toolClasses.get(stack.getItem());
|
||||
if (info == null)
|
||||
{
|
||||
return player.canHarvestBlock(block);
|
||||
}
|
||||
|
||||
Object[] tmp = info.toArray();
|
||||
String toolClass = (String)tmp[0];
|
||||
int harvestLevel = (Integer)tmp[1];
|
||||
|
||||
Integer blockHarvestLevel = toolHarvestLevels.get(Arrays.asList(block, metadata, toolClass));
|
||||
if (blockHarvestLevel == null)
|
||||
{
|
||||
return player.canHarvestBlock(block);
|
||||
}
|
||||
|
||||
if (blockHarvestLevel > harvestLevel)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean canToolHarvestBlock(Block block, int metadata, ItemStack stack)
|
||||
{
|
||||
if (stack == null) return false;
|
||||
List info = toolClasses.get(stack.getItem());
|
||||
if (info == null) return false;
|
||||
|
||||
Object[] tmp = info.toArray();
|
||||
String toolClass = (String)tmp[0];
|
||||
int harvestLevel = (Integer)tmp[1];
|
||||
|
||||
Integer blockHarvestLevel = toolHarvestLevels.get(Arrays.asList(block, metadata, toolClass));
|
||||
return !(blockHarvestLevel == null || blockHarvestLevel > harvestLevel);
|
||||
}
|
||||
|
||||
public static float blockStrength(Block block, EntityPlayer player, World world, int x, int y, int z)
|
||||
{
|
||||
int metadata = world.getBlockMetadata(x, y, z);
|
||||
float hardness = block.getBlockHardness(world, x, y, z);
|
||||
if (hardness < 0.0F)
|
||||
{
|
||||
return 0.0F;
|
||||
}
|
||||
|
||||
if (!canHarvestBlock(block, player, metadata))
|
||||
{
|
||||
float speed = ForgeEventFactory.getBreakSpeed(player, block, metadata, 1.0f);
|
||||
return (speed < 0 ? 0 : speed) / hardness / 100F;
|
||||
}
|
||||
else
|
||||
{
|
||||
return player.getCurrentPlayerStrVsBlock(block, false, metadata) / hardness / 30F;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isToolEffective(ItemStack stack, Block block, int metadata)
|
||||
{
|
||||
List toolClass = toolClasses.get(stack.getItem());
|
||||
return toolClass != null && toolEffectiveness.contains(Arrays.asList(block, metadata, toolClass.get(0)));
|
||||
}
|
||||
|
||||
static void initTools()
|
||||
{
|
||||
if (toolInit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
toolInit = true;
|
||||
|
||||
MinecraftForge.setToolClass(Item.pickaxeWood, "pickaxe", 0);
|
||||
MinecraftForge.setToolClass(Item.pickaxeStone, "pickaxe", 1);
|
||||
MinecraftForge.setToolClass(Item.pickaxeIron, "pickaxe", 2);
|
||||
MinecraftForge.setToolClass(Item.pickaxeGold, "pickaxe", 0);
|
||||
MinecraftForge.setToolClass(Item.pickaxeDiamond, "pickaxe", 3);
|
||||
|
||||
MinecraftForge.setToolClass(Item.axeWood, "axe", 0);
|
||||
MinecraftForge.setToolClass(Item.axeStone, "axe", 1);
|
||||
MinecraftForge.setToolClass(Item.axeIron, "axe", 2);
|
||||
MinecraftForge.setToolClass(Item.axeGold, "axe", 0);
|
||||
MinecraftForge.setToolClass(Item.axeDiamond, "axe", 3);
|
||||
|
||||
MinecraftForge.setToolClass(Item.shovelWood, "shovel", 0);
|
||||
MinecraftForge.setToolClass(Item.shovelStone, "shovel", 1);
|
||||
MinecraftForge.setToolClass(Item.shovelIron, "shovel", 2);
|
||||
MinecraftForge.setToolClass(Item.shovelGold, "shovel", 0);
|
||||
MinecraftForge.setToolClass(Item.shovelDiamond, "shovel", 3);
|
||||
|
||||
for (Block block : ItemPickaxe.blocksEffectiveAgainst)
|
||||
{
|
||||
MinecraftForge.setBlockHarvestLevel(block, "pickaxe", 0);
|
||||
}
|
||||
|
||||
for (Block block : ItemSpade.blocksEffectiveAgainst)
|
||||
{
|
||||
MinecraftForge.setBlockHarvestLevel(block, "shovel", 0);
|
||||
}
|
||||
|
||||
for (Block block : ItemAxe.blocksEffectiveAgainst)
|
||||
{
|
||||
MinecraftForge.setBlockHarvestLevel(block, "axe", 0);
|
||||
}
|
||||
|
||||
MinecraftForge.setBlockHarvestLevel(Block.obsidian, "pickaxe", 3);
|
||||
MinecraftForge.setBlockHarvestLevel(Block.oreEmerald, "pickaxe", 2);
|
||||
MinecraftForge.setBlockHarvestLevel(Block.oreDiamond, "pickaxe", 2);
|
||||
MinecraftForge.setBlockHarvestLevel(Block.blockDiamond, "pickaxe", 2);
|
||||
MinecraftForge.setBlockHarvestLevel(Block.oreGold, "pickaxe", 2);
|
||||
MinecraftForge.setBlockHarvestLevel(Block.blockGold, "pickaxe", 2);
|
||||
MinecraftForge.setBlockHarvestLevel(Block.oreIron, "pickaxe", 1);
|
||||
MinecraftForge.setBlockHarvestLevel(Block.blockIron, "pickaxe", 1);
|
||||
MinecraftForge.setBlockHarvestLevel(Block.oreLapis, "pickaxe", 1);
|
||||
MinecraftForge.setBlockHarvestLevel(Block.blockLapis, "pickaxe", 1);
|
||||
MinecraftForge.setBlockHarvestLevel(Block.oreRedstone, "pickaxe", 2);
|
||||
MinecraftForge.setBlockHarvestLevel(Block.oreRedstoneGlowing, "pickaxe", 2);
|
||||
MinecraftForge.removeBlockEffectiveness(Block.oreRedstone, "pickaxe");
|
||||
MinecraftForge.removeBlockEffectiveness(Block.obsidian, "pickaxe");
|
||||
MinecraftForge.removeBlockEffectiveness(Block.oreRedstoneGlowing, "pickaxe");
|
||||
}
|
||||
|
||||
public static int getTotalArmorValue(EntityPlayer player)
|
||||
{
|
||||
int ret = 0;
|
||||
for (int x = 0; x < player.inventory.armorInventory.length; x++)
|
||||
{
|
||||
ItemStack stack = player.inventory.armorInventory[x];
|
||||
if (stack != null && stack.getItem() instanceof ISpecialArmor)
|
||||
{
|
||||
ret += ((ISpecialArmor)stack.getItem()).getArmorDisplay(player, stack, x);
|
||||
}
|
||||
else if (stack != null && stack.getItem() instanceof ItemArmor)
|
||||
{
|
||||
ret += ((ItemArmor)stack.getItem()).damageReduceAmount;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static
|
||||
{
|
||||
grassList.add(new GrassEntry(Block.plantYellow, 0, 20));
|
||||
grassList.add(new GrassEntry(Block.plantRed, 0, 10));
|
||||
seedList.add(new SeedEntry(new ItemStack(Item.seeds), 10));
|
||||
initTools();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a player uses 'pick block', calls new Entity and Block hooks.
|
||||
*/
|
||||
public static boolean onPickBlock(MovingObjectPosition target, EntityPlayer player, World world)
|
||||
{
|
||||
ItemStack result = null;
|
||||
boolean isCreative = player.capabilities.isCreativeMode;
|
||||
|
||||
if (target.typeOfHit == EnumMovingObjectType.TILE)
|
||||
{
|
||||
int x = target.blockX;
|
||||
int y = target.blockY;
|
||||
int z = target.blockZ;
|
||||
Block var8 = Block.blocksList[world.getBlockId(x, y, z)];
|
||||
|
||||
if (var8 == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
result = var8.getPickBlock(target, world, x, y, z);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (target.typeOfHit != EnumMovingObjectType.ENTITY || target.entityHit == null || !isCreative)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
result = target.entityHit.getPickedResult(target);
|
||||
}
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int x = 0; x < 9; x++)
|
||||
{
|
||||
ItemStack stack = player.inventory.getStackInSlot(x);
|
||||
if (stack != null && stack.isItemEqual(result) && ItemStack.areItemStackTagsEqual(stack, result))
|
||||
{
|
||||
player.inventory.currentItem = x;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isCreative)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int slot = player.inventory.getFirstEmptyStack();
|
||||
if (slot < 0 || slot >= 9)
|
||||
{
|
||||
slot = player.inventory.currentItem;
|
||||
}
|
||||
|
||||
player.inventory.setInventorySlotContents(slot, result);
|
||||
player.inventory.currentItem = slot;
|
||||
return true;
|
||||
}
|
||||
|
||||
//Optifine Helper Functions u.u, these are here specifically for Optifine
|
||||
//Note: When using Optfine, these methods are invoked using reflection, which
|
||||
//incurs a major performance penalty.
|
||||
public static void onLivingSetAttackTarget(EntityLivingBase entity, EntityLivingBase target)
|
||||
{
|
||||
MinecraftForge.EVENT_BUS.post(new LivingSetAttackTargetEvent(entity, target));
|
||||
}
|
||||
|
||||
public static boolean onLivingUpdate(EntityLivingBase entity)
|
||||
{
|
||||
return MinecraftForge.EVENT_BUS.post(new LivingUpdateEvent(entity));
|
||||
}
|
||||
|
||||
public static boolean onLivingAttack(EntityLivingBase entity, DamageSource src, float amount)
|
||||
{
|
||||
return MinecraftForge.EVENT_BUS.post(new LivingAttackEvent(entity, src, amount));
|
||||
}
|
||||
|
||||
public static float onLivingHurt(EntityLivingBase entity, DamageSource src, float amount)
|
||||
{
|
||||
LivingHurtEvent event = new LivingHurtEvent(entity, src, amount);
|
||||
return (MinecraftForge.EVENT_BUS.post(event) ? 0 : event.ammount);
|
||||
}
|
||||
|
||||
public static boolean onLivingDeath(EntityLivingBase entity, DamageSource src)
|
||||
{
|
||||
return MinecraftForge.EVENT_BUS.post(new LivingDeathEvent(entity, src));
|
||||
}
|
||||
|
||||
public static boolean onLivingDrops(EntityLivingBase entity, DamageSource source, ArrayList<EntityItem> drops, int lootingLevel, boolean recentlyHit, int specialDropValue)
|
||||
{
|
||||
return MinecraftForge.EVENT_BUS.post(new LivingDropsEvent(entity, source, drops, lootingLevel, recentlyHit, specialDropValue));
|
||||
}
|
||||
|
||||
public static float onLivingFall(EntityLivingBase entity, float distance)
|
||||
{
|
||||
LivingFallEvent event = new LivingFallEvent(entity, distance);
|
||||
return (MinecraftForge.EVENT_BUS.post(event) ? 0.0f : event.distance);
|
||||
}
|
||||
|
||||
public static boolean isLivingOnLadder(Block block, World world, int x, int y, int z, EntityLivingBase entity)
|
||||
{
|
||||
return block != null && block.isLadder(world, x, y, z, entity);
|
||||
}
|
||||
|
||||
public static void onLivingJump(EntityLivingBase entity)
|
||||
{
|
||||
MinecraftForge.EVENT_BUS.post(new LivingJumpEvent(entity));
|
||||
}
|
||||
|
||||
public static EntityItem onPlayerTossEvent(EntityPlayer player, ItemStack item)
|
||||
{
|
||||
player.captureDrops = true;
|
||||
EntityItem ret = player.dropPlayerItemWithRandomChoice(item, false);
|
||||
player.capturedDrops.clear();
|
||||
player.captureDrops = false;
|
||||
|
||||
ItemTossEvent event = new ItemTossEvent(ret, player);
|
||||
if (MinecraftForge.EVENT_BUS.post(event))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
player.joinEntityItemWithWorld(event.entityItem);
|
||||
return event.entityItem;
|
||||
}
|
||||
|
||||
public static float getEnchantPower(World world, int x, int y, int z)
|
||||
{
|
||||
if (world.isAirBlock(x, y, z))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
Block block = Block.blocksList[world.getBlockId(x, y, z)];
|
||||
return (block == null ? 0 : block.getEnchantPowerBonus(world, x, y, z));
|
||||
}
|
||||
|
||||
public static ChatMessageComponent onServerChatEvent(NetServerHandler net, String raw, ChatMessageComponent comp)
|
||||
{
|
||||
ServerChatEvent event = new ServerChatEvent(net.playerEntity, raw, comp);
|
||||
if (MinecraftForge.EVENT_BUS.post(event))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return event.component;
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
package net.minecraftforge.common;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import cpw.mods.fml.common.FMLLog;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.item.EntityItem;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.event.*;
|
||||
import net.minecraftforge.event.entity.*;
|
||||
import net.minecraftforge.event.world.WorldEvent;
|
||||
|
||||
public class ForgeInternalHandler
|
||||
{
|
||||
@ForgeSubscribe(priority = EventPriority.HIGHEST)
|
||||
public void onEntityJoinWorld(EntityJoinWorldEvent event)
|
||||
{
|
||||
if (!event.world.isRemote)
|
||||
{
|
||||
ForgeChunkManager.loadEntity(event.entity);
|
||||
}
|
||||
|
||||
Entity entity = event.entity;
|
||||
if (entity.getClass().equals(EntityItem.class))
|
||||
{
|
||||
ItemStack stack = entity.getDataWatcher().getWatchableObjectItemStack(10);
|
||||
|
||||
if (stack == null)
|
||||
{
|
||||
//entity.setDead();
|
||||
//event.setCanceled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
Item item = stack.getItem();
|
||||
if (item == null)
|
||||
{
|
||||
FMLLog.warning("Attempted to add a EntityItem to the world with a invalid item: ID %d at " +
|
||||
"(%2.2f, %2.2f, %2.2f), this is most likely a config issue between you and the server. Please double check your configs",
|
||||
stack.itemID, entity.posX, entity.posY, entity.posZ);
|
||||
entity.setDead();
|
||||
event.setCanceled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.hasCustomEntity(stack))
|
||||
{
|
||||
Entity newEntity = item.createEntity(event.world, entity, stack);
|
||||
if (newEntity != null)
|
||||
{
|
||||
entity.setDead();
|
||||
event.setCanceled(true);
|
||||
event.world.spawnEntityInWorld(newEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ForgeSubscribe(priority = EventPriority.HIGHEST)
|
||||
public void onDimensionLoad(WorldEvent.Load event)
|
||||
{
|
||||
ForgeChunkManager.loadWorld(event.world);
|
||||
}
|
||||
|
||||
@ForgeSubscribe(priority = EventPriority.HIGHEST)
|
||||
public void onDimensionSave(WorldEvent.Save event)
|
||||
{
|
||||
ForgeChunkManager.saveWorld(event.world);
|
||||
}
|
||||
|
||||
@ForgeSubscribe(priority = EventPriority.HIGHEST)
|
||||
public void onDimensionUnload(WorldEvent.Unload event)
|
||||
{
|
||||
ForgeChunkManager.unloadWorld(event.world);
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/**
|
||||
* This software is provided under the terms of the Minecraft Forge Public
|
||||
* License v1.0.
|
||||
*/
|
||||
|
||||
package net.minecraftforge.common;
|
||||
|
||||
public class ForgeVersion
|
||||
{
|
||||
//This number is incremented every time we remove deprecated code/major API changes, never reset
|
||||
public static final int majorVersion = 8;
|
||||
//This number is incremented every minecraft release, never reset
|
||||
public static final int minorVersion = 9;
|
||||
//This number is incremented every time a interface changes or new major feature is added, and reset every Minecraft version
|
||||
public static final int revisionVersion = 0;
|
||||
//This number is incremented every time Jenkins builds Forge, and never reset. Should always be 0 in the repo code.
|
||||
public static final int buildVersion = 0;
|
||||
|
||||
public static int getMajorVersion()
|
||||
{
|
||||
return majorVersion;
|
||||
}
|
||||
|
||||
public static int getMinorVersion()
|
||||
{
|
||||
return minorVersion;
|
||||
}
|
||||
|
||||
public static int getRevisionVersion()
|
||||
{
|
||||
return revisionVersion;
|
||||
}
|
||||
|
||||
public static int getBuildVersion()
|
||||
{
|
||||
return buildVersion;
|
||||
}
|
||||
|
||||
public static String getVersion()
|
||||
{
|
||||
return String.format("%d.%d.%d.%d", majorVersion, minorVersion, revisionVersion, buildVersion);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
package net.minecraftforge.common;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
/**
|
||||
* Allows for custom entity data and logic to be hooked to existing entity classes.
|
||||
*
|
||||
* @author cpw, mithion
|
||||
*
|
||||
*/
|
||||
public interface IExtendedEntityProperties {
|
||||
/**
|
||||
* Called when the entity that this class is attached to is saved.
|
||||
* Any custom entity data that needs saving should be saved here.
|
||||
* @param compound The compound to save to.
|
||||
*/
|
||||
public void saveNBTData(NBTTagCompound compound);
|
||||
|
||||
/**
|
||||
* Called when the entity that this class is attached to is loaded.
|
||||
* In order to hook into this, you will need to subscribe to the EntityConstructing event.
|
||||
* Otherwise, you will need to initialize manually.
|
||||
* @param compound The compound to load from.
|
||||
*/
|
||||
public void loadNBTData(NBTTagCompound compound);
|
||||
|
||||
/**
|
||||
* Used to initialize the extended properties with the entity that this is attached to, as well
|
||||
* as the world object.
|
||||
* Called automatically if you register with the EntityConstructing event.
|
||||
* @param entity The entity that this extended properties is attached to
|
||||
* @param world The world in which the entity exists
|
||||
*/
|
||||
public void init(Entity entity, World world);
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package net.minecraftforge.common;
|
||||
|
||||
import net.minecraft.util.AxisAlignedBB;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.item.EntityMinecart;
|
||||
|
||||
/**
|
||||
* This class defines a replacement for the default minecart collision code.
|
||||
* Only one handler can be registered at a time. It it registered with EntityMinecart.registerCollisionHandler().
|
||||
* If you use this, make it a configuration option.
|
||||
* @author CovertJaguar
|
||||
*/
|
||||
public interface IMinecartCollisionHandler
|
||||
{
|
||||
|
||||
/**
|
||||
* This basically replaces the function of the same name in EnityMinecart.
|
||||
* Code in IMinecartHooks.applyEntityCollisionHook is still run.
|
||||
* @param cart The cart that called the collision.
|
||||
* @param other The object it collided with.
|
||||
*/
|
||||
public void onEntityCollision(EntityMinecart cart, Entity other);
|
||||
|
||||
/**
|
||||
* This function replaced the function of the same name in EntityMinecart.
|
||||
* It is used to define whether minecarts collide with specific entities,
|
||||
* for example items.
|
||||
* @param cart The cart for which the collision box was requested.
|
||||
* @param other The entity requesting the collision box.
|
||||
* @return The collision box or null.
|
||||
*/
|
||||
public AxisAlignedBB getCollisionBox(EntityMinecart cart, Entity other);
|
||||
|
||||
/**
|
||||
* This function is used to define the box used for detecting minecart collisions.
|
||||
* It is generally bigger that the normal collision box.
|
||||
* @param cart The cart for which the collision box was requested.
|
||||
* @return The collision box, cannot be null.
|
||||
*/
|
||||
public AxisAlignedBB getMinecartCollisionBox(EntityMinecart cart);
|
||||
|
||||
/**
|
||||
* This function replaces the function of the same name in EntityMinecart.
|
||||
* It defines whether minecarts are solid to the player.
|
||||
* @param cart The cart for which the bounding box was requested.
|
||||
* @return The bounding box or null.
|
||||
*/
|
||||
public AxisAlignedBB getBoundingBox(EntityMinecart cart);
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
package net.minecraftforge.common;
|
||||
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public interface IPlantable
|
||||
{
|
||||
public EnumPlantType getPlantType(World world, int x, int y, int z);
|
||||
public int getPlantID(World world, int x, int y, int z);
|
||||
public int getPlantMetadata(World world, int x, int y, int z);
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package net.minecraftforge.common;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.MovingObjectPosition;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
/**
|
||||
*
|
||||
* This allows for mods to create there own Shear-like items
|
||||
* and have them interact with Blocks/Entities without extra work.
|
||||
* Also, if your block/entity supports the Shears, this allows you
|
||||
* to support mod-shears as well.
|
||||
*
|
||||
*/
|
||||
public interface IShearable
|
||||
{
|
||||
/**
|
||||
* Checks if the object is currently shearable
|
||||
* Example: Sheep return false when they have no wool
|
||||
*
|
||||
* @param item The itemstack that is being used, Possible to be null
|
||||
* @param world The current world
|
||||
* @param x The X Position
|
||||
* @param y The Y Position
|
||||
* @param z The Z Position
|
||||
* @return If this is shearable, and onSheared should be called.
|
||||
*/
|
||||
public boolean isShearable(ItemStack item, World world, int x, int y, int z);
|
||||
|
||||
/**
|
||||
* Performs the shear function on this object.
|
||||
* This is called for both client, and server.
|
||||
* The object should perform all actions related to being sheared,
|
||||
* except for dropping of the items.
|
||||
*
|
||||
* Returns a list of items that resulted from the shearing process.
|
||||
*
|
||||
* For entities, they should trust there internal location information
|
||||
* over the values passed into this function.
|
||||
*
|
||||
* @param item The itemstack that is being used, Possible to be null
|
||||
* @param world The current world
|
||||
* @param x The X Position
|
||||
* @param y The Y Position
|
||||
* @param z The Z Position
|
||||
* @param fortune The fortune level of the shears being used
|
||||
* @return A ArrayList containing all items from this shearing. Possible to be null.
|
||||
*/
|
||||
public ArrayList<ItemStack> onSheared(ItemStack item, World world, int x, int y, int z, int fortune);
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/**
|
||||
* This software is provided under the terms of the Minecraft Forge Public
|
||||
* License v1.0.
|
||||
*/
|
||||
|
||||
package net.minecraftforge.common;
|
||||
|
||||
import net.minecraft.inventory.IInventory;
|
||||
|
||||
/** Inventory ranges mapped by side. This class is implemented by TileEntities
|
||||
* that provide different inventory slot ranges to different sides.
|
||||
*/
|
||||
@Deprecated //A equivalent Interface is now in Minecraft Vanilla will be removed next major MC version
|
||||
public interface ISidedInventory extends IInventory
|
||||
{
|
||||
|
||||
/**
|
||||
* Get the start of the side inventory.
|
||||
* @param side The global side to get the start of range.
|
||||
*/
|
||||
@Deprecated
|
||||
int getStartInventorySide(ForgeDirection side);
|
||||
|
||||
/**
|
||||
* Get the size of the side inventory.
|
||||
* @param side The global side.
|
||||
*/
|
||||
@Deprecated
|
||||
int getSizeInventorySide(ForgeDirection side);
|
||||
}
|
||||
|
||||
@@ -1,317 +0,0 @@
|
||||
/**
|
||||
* This software is provided under the terms of the Minecraft Forge Public
|
||||
* License v1.0.
|
||||
*/
|
||||
|
||||
package net.minecraftforge.common;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.ItemArmor;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
/**
|
||||
* This interface is to be implemented by ItemArmor classes. It will allow to
|
||||
* modify computation of damage and health loss. Computation will be called
|
||||
* before the actual armor computation, which can then be cancelled.
|
||||
*
|
||||
* @see ItemArmor
|
||||
*/
|
||||
public interface ISpecialArmor
|
||||
{
|
||||
/**
|
||||
* Retrieves the modifiers to be used when calculating armor damage.
|
||||
*
|
||||
* Armor will higher priority will have damage applied to them before
|
||||
* lower priority ones. If there are multiple pieces of armor with the
|
||||
* same priority, damage will be distributed between them based on there
|
||||
* absorption ratio.
|
||||
*
|
||||
* @param player The entity wearing the armor.
|
||||
* @param armor The ItemStack of the armor item itself.
|
||||
* @param source The source of the damage, which can be used to alter armor
|
||||
* properties based on the type or source of damage.
|
||||
* @param damage The total damage being applied to the entity
|
||||
* @param slot The armor slot the item is in.
|
||||
* @return A ArmorProperties instance holding information about how the armor effects damage.
|
||||
*/
|
||||
public ArmorProperties getProperties(EntityLivingBase player, ItemStack armor, DamageSource source, double damage, int slot);
|
||||
|
||||
/**
|
||||
* Get the displayed effective armor.
|
||||
*
|
||||
* @param player The player wearing the armor.
|
||||
* @param armor The ItemStack of the armor item itself.
|
||||
* @param slot The armor slot the item is in.
|
||||
* @return The number of armor points for display, 2 per shield.
|
||||
*/
|
||||
public abstract int getArmorDisplay(EntityPlayer player, ItemStack armor, int slot);
|
||||
|
||||
/**
|
||||
* Applies damage to the ItemStack. The mod is responsible for reducing the
|
||||
* item durability and stack size. If the stack is depleted it will be cleaned
|
||||
* up automatically.
|
||||
*
|
||||
* @param entity The entity wearing the armor
|
||||
* @param stack The ItemStack of the armor item itself.
|
||||
* @param source The source of the damage, which can be used to alter armor
|
||||
* properties based on the type or source of damage.
|
||||
* @param damage The amount of damage being applied to the armor
|
||||
* @param slot The armor slot the item is in.
|
||||
*/
|
||||
public abstract void damageArmor(EntityLivingBase entity, ItemStack stack, DamageSource source, int damage, int slot);
|
||||
|
||||
public static class ArmorProperties implements Comparable<ArmorProperties>
|
||||
{
|
||||
public int Priority = 0;
|
||||
public int AbsorbMax = Integer.MAX_VALUE;
|
||||
public double AbsorbRatio = 0;
|
||||
public int Slot = 0;
|
||||
private static final boolean DEBUG = false; //Only enable this if you wish to be spamed with debugging information.
|
||||
//Left it in because I figured it'd be useful for modders developing custom armor.
|
||||
|
||||
public ArmorProperties(int priority, double ratio, int max)
|
||||
{
|
||||
Priority = priority;
|
||||
AbsorbRatio = ratio;
|
||||
AbsorbMax = max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gathers and applies armor reduction to damage being dealt to a entity.
|
||||
*
|
||||
* @param entity The Entity being damage
|
||||
* @param inventory An array of armor items
|
||||
* @param source The damage source type
|
||||
* @param damage The total damage being done
|
||||
* @return The left over damage that has not been absorbed by the armor
|
||||
*/
|
||||
public static float ApplyArmor(EntityLivingBase entity, ItemStack[] inventory, DamageSource source, double damage)
|
||||
{
|
||||
if (DEBUG)
|
||||
{
|
||||
System.out.println("Start: " + damage + " " + (damage * 25));
|
||||
}
|
||||
damage *= 25;
|
||||
ArrayList<ArmorProperties> dmgVals = new ArrayList<ArmorProperties>();
|
||||
for (int x = 0; x < inventory.length; x++)
|
||||
{
|
||||
ItemStack stack = inventory[x];
|
||||
if (stack == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
ArmorProperties prop = null;
|
||||
if (stack.getItem() instanceof ISpecialArmor)
|
||||
{
|
||||
ISpecialArmor armor = (ISpecialArmor)stack.getItem();
|
||||
prop = armor.getProperties(entity, stack, source, damage / 25D, x).copy();
|
||||
}
|
||||
else if (stack.getItem() instanceof ItemArmor && !source.isUnblockable())
|
||||
{
|
||||
ItemArmor armor = (ItemArmor)stack.getItem();
|
||||
prop = new ArmorProperties(0, armor.damageReduceAmount / 25D, armor.getMaxDamage() + 1 - stack.getItemDamage());
|
||||
}
|
||||
if (prop != null)
|
||||
{
|
||||
prop.Slot = x;
|
||||
dmgVals.add(prop);
|
||||
}
|
||||
}
|
||||
if (dmgVals.size() > 0)
|
||||
{
|
||||
ArmorProperties[] props = dmgVals.toArray(new ArmorProperties[dmgVals.size()]);
|
||||
StandardizeList(props, damage);
|
||||
int level = props[0].Priority;
|
||||
double ratio = 0;
|
||||
for (ArmorProperties prop : props)
|
||||
{
|
||||
if (level != prop.Priority)
|
||||
{
|
||||
damage -= (damage * ratio);
|
||||
ratio = 0;
|
||||
level = prop.Priority;
|
||||
}
|
||||
ratio += prop.AbsorbRatio;
|
||||
|
||||
double absorb = damage * prop.AbsorbRatio;
|
||||
if (absorb > 0)
|
||||
{
|
||||
ItemStack stack = inventory[prop.Slot];
|
||||
int itemDamage = (int)(absorb / 25D < 1 ? 1 : absorb / 25D);
|
||||
if (stack.getItem() instanceof ISpecialArmor)
|
||||
{
|
||||
((ISpecialArmor)stack.getItem()).damageArmor(entity, stack, source, itemDamage, prop.Slot);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (DEBUG)
|
||||
{
|
||||
System.out.println("Item: " + stack.toString() + " Absorbed: " + (absorb / 25D) + " Damaged: " + itemDamage);
|
||||
}
|
||||
stack.damageItem(itemDamage, entity);
|
||||
}
|
||||
if (stack.stackSize <= 0)
|
||||
{
|
||||
/*if (entity instanceof EntityPlayer)
|
||||
{
|
||||
stack.onItemDestroyedByUse((EntityPlayer)entity);
|
||||
}*/
|
||||
inventory[prop.Slot] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
damage -= (damage * ratio);
|
||||
}
|
||||
if (DEBUG)
|
||||
{
|
||||
System.out.println("Return: " + (int)(damage / 25.0F) + " " + damage);
|
||||
}
|
||||
return (float)(damage / 25.0F);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts and standardizes the distribution of damage over armor.
|
||||
*
|
||||
* @param armor The armor information
|
||||
* @param damage The total damage being received
|
||||
*/
|
||||
private static void StandardizeList(ArmorProperties[] armor, double damage)
|
||||
{
|
||||
Arrays.sort(armor);
|
||||
|
||||
int start = 0;
|
||||
double total = 0;
|
||||
int priority = armor[0].Priority;
|
||||
int pStart = 0;
|
||||
boolean pChange = false;
|
||||
boolean pFinished = false;
|
||||
|
||||
if (DEBUG)
|
||||
{
|
||||
for (ArmorProperties prop : armor)
|
||||
{
|
||||
System.out.println(prop);
|
||||
}
|
||||
System.out.println("========================");
|
||||
}
|
||||
|
||||
for (int x = 0; x < armor.length; x++)
|
||||
{
|
||||
total += armor[x].AbsorbRatio;
|
||||
if (x == armor.length - 1 || armor[x].Priority != priority)
|
||||
{
|
||||
if (armor[x].Priority != priority)
|
||||
{
|
||||
total -= armor[x].AbsorbRatio;
|
||||
x--;
|
||||
pChange = true;
|
||||
}
|
||||
if (total > 1)
|
||||
{
|
||||
for (int y = start; y <= x; y++)
|
||||
{
|
||||
double newRatio = armor[y].AbsorbRatio / total;
|
||||
if (newRatio * damage > armor[y].AbsorbMax)
|
||||
{
|
||||
armor[y].AbsorbRatio = (double)armor[y].AbsorbMax / damage;
|
||||
total = 0;
|
||||
for (int z = pStart; z <= y; z++)
|
||||
{
|
||||
total += armor[z].AbsorbRatio;
|
||||
}
|
||||
start = y + 1;
|
||||
x = y;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
armor[y].AbsorbRatio = newRatio;
|
||||
pFinished = true;
|
||||
}
|
||||
}
|
||||
if (pChange && pFinished)
|
||||
{
|
||||
damage -= (damage * total);
|
||||
total = 0;
|
||||
start = x + 1;
|
||||
priority = armor[start].Priority;
|
||||
pStart = start;
|
||||
pChange = false;
|
||||
pFinished = false;
|
||||
if (damage <= 0)
|
||||
{
|
||||
for (int y = x + 1; y < armor.length; y++)
|
||||
{
|
||||
armor[y].AbsorbRatio = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int y = start; y <= x; y++)
|
||||
{
|
||||
total -= armor[y].AbsorbRatio;
|
||||
if (damage * armor[y].AbsorbRatio > armor[y].AbsorbMax)
|
||||
{
|
||||
armor[y].AbsorbRatio = (double)armor[y].AbsorbMax / damage;
|
||||
}
|
||||
total += armor[y].AbsorbRatio;
|
||||
}
|
||||
damage -= (damage * total);
|
||||
total = 0;
|
||||
if (x != armor.length - 1)
|
||||
{
|
||||
start = x + 1;
|
||||
priority = armor[start].Priority;
|
||||
pStart = start;
|
||||
pChange = false;
|
||||
if (damage <= 0)
|
||||
{
|
||||
for (int y = x + 1; y < armor.length; y++)
|
||||
{
|
||||
armor[y].AbsorbRatio = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (DEBUG)
|
||||
{
|
||||
for (ArmorProperties prop : armor)
|
||||
{
|
||||
System.out.println(prop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int compareTo(ArmorProperties o)
|
||||
{
|
||||
if (o.Priority != Priority)
|
||||
{
|
||||
return o.Priority - Priority;
|
||||
}
|
||||
double left = ( AbsorbRatio == 0 ? 0 : AbsorbMax * 100.0D / AbsorbRatio);
|
||||
double right = (o.AbsorbRatio == 0 ? 0 : o.AbsorbMax * 100.0D / o.AbsorbRatio);
|
||||
return (int)(left - right);
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%d, %d, %f, %d", Priority, AbsorbMax, AbsorbRatio, (AbsorbRatio == 0 ? 0 : (int)(AbsorbMax * 100.0D / AbsorbRatio)));
|
||||
}
|
||||
|
||||
public ArmorProperties copy()
|
||||
{
|
||||
return new ArmorProperties(Priority, AbsorbRatio, AbsorbMax);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,220 +0,0 @@
|
||||
package net.minecraftforge.common;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.*;
|
||||
|
||||
import cpw.mods.fml.common.FMLCommonHandler;
|
||||
import cpw.mods.fml.common.FMLLog;
|
||||
import cpw.mods.fml.common.Mod;
|
||||
import cpw.mods.fml.common.ModContainer;
|
||||
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.client.renderer.texture.IconRegister;
|
||||
import net.minecraft.crash.CrashReport;
|
||||
import net.minecraft.entity.monster.EntityEnderman;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.common.ForgeHooks.GrassEntry;
|
||||
import net.minecraftforge.common.ForgeHooks.SeedEntry;
|
||||
import net.minecraftforge.event.EventBus;
|
||||
import net.minecraftforge.event.ForgeSubscribe;
|
||||
import net.minecraftforge.event.entity.EntityEvent;
|
||||
import net.minecraftforge.oredict.OreDictionary;
|
||||
|
||||
public class MinecraftForge
|
||||
{
|
||||
/**
|
||||
* The core Forge EventBusses, all events for Forge will be fired on these,
|
||||
* you should use this to register all your listeners.
|
||||
* This replaces every register*Handler() function in the old version of Forge.
|
||||
* TERRAIN_GEN_BUS for terrain gen events
|
||||
* ORE_GEN_BUS for ore gen events
|
||||
* EVENT_BUS for everything else
|
||||
*/
|
||||
public static final EventBus EVENT_BUS = new EventBus();
|
||||
public static final EventBus TERRAIN_GEN_BUS = new EventBus();
|
||||
public static final EventBus ORE_GEN_BUS = new EventBus();
|
||||
|
||||
private static final ForgeInternalHandler INTERNAL_HANDLER = new ForgeInternalHandler();
|
||||
|
||||
|
||||
/** Register a new plant to be planted when bonemeal is used on grass.
|
||||
* @param block The block to place.
|
||||
* @param metadata The metadata to set for the block when being placed.
|
||||
* @param weight The weight of the plant, where red flowers are
|
||||
* 10 and yellow flowers are 20.
|
||||
*/
|
||||
public static void addGrassPlant(Block block, int metadata, int weight)
|
||||
{
|
||||
ForgeHooks.grassList.add(new GrassEntry(block, metadata, weight));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new seed to be dropped when breaking tall grass.
|
||||
*
|
||||
* @param seed The item to drop as a seed.
|
||||
* @param weight The relative probability of the seeds,
|
||||
* where wheat seeds are 10.
|
||||
*/
|
||||
public static void addGrassSeed(ItemStack seed, int weight)
|
||||
{
|
||||
ForgeHooks.seedList.add(new SeedEntry(seed, weight));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Register a tool as a tool class with a given harvest level.
|
||||
*
|
||||
* @param tool The custom tool to register.
|
||||
* @param toolClass The tool class to register as. The predefined tool
|
||||
* clases are "pickaxe", "shovel", "axe". You can add
|
||||
* others for custom tools.
|
||||
* @param harvestLevel The harvest level of the tool.
|
||||
*/
|
||||
public static void setToolClass(Item tool, String toolClass, int harvestLevel)
|
||||
{
|
||||
ForgeHooks.toolClasses.put(tool, Arrays.asList(toolClass, harvestLevel));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a block to be harvested by a tool class. This is the metadata
|
||||
* sensitive version, use it if your blocks are using metadata variants.
|
||||
* By default, this sets the block class as effective against that type.
|
||||
*
|
||||
* @param block The block to register.
|
||||
* @param metadata The metadata for the block subtype.
|
||||
* @param toolClass The tool class to register as able to remove this block.
|
||||
* You may register the same block multiple times with different tool
|
||||
* classes, if multiple tool types can be used to harvest this block.
|
||||
* @param harvestLevel The minimum tool harvest level required to successfully
|
||||
* harvest the block.
|
||||
* @see MinecraftForge#setToolClass for details on tool classes.
|
||||
*/
|
||||
public static void setBlockHarvestLevel(Block block, int metadata, String toolClass, int harvestLevel)
|
||||
{
|
||||
List key = Arrays.asList(block, metadata, toolClass);
|
||||
ForgeHooks.toolHarvestLevels.put(key, harvestLevel);
|
||||
ForgeHooks.toolEffectiveness.add(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a block effectiveness mapping. Since setBlockHarvestLevel
|
||||
* makes the tool class effective against the block by default, this can be
|
||||
* used to remove that mapping. This will force a block to be harvested at
|
||||
* the same speed regardless of tool quality, while still requiring a given
|
||||
* harvesting level.
|
||||
*
|
||||
* @param block The block to remove effectiveness from.
|
||||
* @param metadata The metadata for the block subtype.
|
||||
* @param toolClass The tool class to remove the effectiveness mapping from.
|
||||
* @see MinecraftForge#setToolClass for details on tool classes.
|
||||
*/
|
||||
public static void removeBlockEffectiveness(Block block, int metadata, String toolClass)
|
||||
{
|
||||
List key = Arrays.asList(block, metadata, toolClass);
|
||||
ForgeHooks.toolEffectiveness.remove(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a block to be harvested by a tool class.
|
||||
* By default, this sets the block class as effective against that type.
|
||||
*
|
||||
* @param block The block to register.
|
||||
* @param toolClass The tool class to register as able to remove this block.
|
||||
* You may register the same block multiple times with different tool
|
||||
* classes, if multiple tool types can be used to harvest this block.
|
||||
* @param harvestLevel The minimum tool harvest level required to successfully
|
||||
* harvest the block.
|
||||
* @see MinecraftForge#setToolClass for details on tool classes.
|
||||
*/
|
||||
public static void setBlockHarvestLevel(Block block, String toolClass, int harvestLevel)
|
||||
{
|
||||
for (int metadata = 0; metadata < 16; metadata++)
|
||||
{
|
||||
List key = Arrays.asList(block, metadata, toolClass);
|
||||
ForgeHooks.toolHarvestLevels.put(key, harvestLevel);
|
||||
ForgeHooks.toolEffectiveness.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the block harvest level for a particular tool class.
|
||||
*
|
||||
* @param block The block to check.
|
||||
* @param metadata The metadata for the block subtype.
|
||||
* @param toolClass The tool class to check as able to remove this block.
|
||||
* @see MinecraftForge#setToolClass for details on tool classes.
|
||||
* @return The harvest level or -1 if no mapping exists.
|
||||
*/
|
||||
public static int getBlockHarvestLevel(Block block, int metadata, String toolClass)
|
||||
{
|
||||
ForgeHooks.initTools();
|
||||
List key = Arrays.asList(block, metadata, toolClass);
|
||||
Integer harvestLevel = ForgeHooks.toolHarvestLevels.get(key);
|
||||
return (harvestLevel == null ? -1 : harvestLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a block effectiveness mapping. Since setBlockHarvestLevel
|
||||
* makes the tool class effective against the block by default, this can be
|
||||
* used to remove that mapping. This will force a block to be harvested at
|
||||
* the same speed regardless of tool quality, while still requiring a given
|
||||
* harvesting level.
|
||||
*
|
||||
* @param block The block to remove effectiveness from.
|
||||
* @param toolClass The tool class to remove the effectiveness mapping from.
|
||||
* @see MinecraftForge#setToolClass for details on tool classes.
|
||||
*/
|
||||
public static void removeBlockEffectiveness(Block block, String toolClass)
|
||||
{
|
||||
for (int metadata = 0; metadata < 16; metadata++)
|
||||
{
|
||||
List key = Arrays.asList(block, metadata, toolClass);
|
||||
ForgeHooks.toolEffectiveness.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method invoked by FML before any other mods are loaded.
|
||||
*/
|
||||
public static void initialize()
|
||||
{
|
||||
System.out.printf("MinecraftForge v%s Initialized\n", ForgeVersion.getVersion());
|
||||
FMLLog.info("MinecraftForge v%s Initialized", ForgeVersion.getVersion());
|
||||
|
||||
Block filler = new Block(0, Material.air)
|
||||
{
|
||||
@SideOnly(Side.CLIENT) public void func_94332_a(IconRegister register){}
|
||||
};
|
||||
Block.blocksList[0] = null;
|
||||
Block.opaqueCubeLookup[0] = false;
|
||||
Block.lightOpacity[0] = 0;
|
||||
filler.setUnlocalizedName("ForgeFiller");
|
||||
|
||||
for (int x = 256; x < 4096; x++)
|
||||
{
|
||||
if (Item.itemsList[x] != null)
|
||||
{
|
||||
Block.blocksList[x] = filler;
|
||||
}
|
||||
}
|
||||
|
||||
boolean[] temp = new boolean[4096];
|
||||
System.arraycopy(EntityEnderman.carriableBlocks, 0, temp, 0, EntityEnderman.carriableBlocks.length);
|
||||
EntityEnderman.carriableBlocks = temp;
|
||||
|
||||
EVENT_BUS.register(INTERNAL_HANDLER);
|
||||
OreDictionary.getOreName(0);
|
||||
|
||||
//Force these classes to be defined, Should prevent derp error hiding.
|
||||
new CrashReport("ThisIsFake", new Exception("Not real"));
|
||||
}
|
||||
|
||||
public static String getBrandingVersion()
|
||||
{
|
||||
return "Minecraft Forge "+ ForgeVersion.getVersion();
|
||||
}
|
||||
}
|
||||
@@ -1,409 +0,0 @@
|
||||
/**
|
||||
* This software is provided under the terms of the Minecraft Forge Public
|
||||
* License v1.0.
|
||||
*/
|
||||
|
||||
package net.minecraftforge.common;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import net.minecraftforge.common.Property.Type;
|
||||
|
||||
public class Property
|
||||
{
|
||||
public enum Type
|
||||
{
|
||||
STRING,
|
||||
INTEGER,
|
||||
BOOLEAN,
|
||||
DOUBLE;
|
||||
|
||||
private static Type[] values = {STRING, INTEGER, BOOLEAN, DOUBLE};
|
||||
|
||||
public static Type tryParse(char id)
|
||||
{
|
||||
for (int x = 0; x < values.length; x++)
|
||||
{
|
||||
if (values[x].getID() == id)
|
||||
{
|
||||
return values[x];
|
||||
}
|
||||
}
|
||||
|
||||
return STRING;
|
||||
}
|
||||
|
||||
public char getID()
|
||||
{
|
||||
return name().charAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
private String name;
|
||||
private String value;
|
||||
public String comment;
|
||||
private String[] values;
|
||||
|
||||
private final boolean wasRead;
|
||||
private final boolean isList;
|
||||
private final Type type;
|
||||
private boolean changed = false;
|
||||
|
||||
public Property()
|
||||
{
|
||||
wasRead = false;
|
||||
type = null;
|
||||
isList = false;
|
||||
}
|
||||
|
||||
public Property(String name, String value, Type type)
|
||||
{
|
||||
this(name, value, type, false);
|
||||
}
|
||||
|
||||
Property(String name, String value, Type type, boolean read)
|
||||
{
|
||||
setName(name);
|
||||
this.value = value;
|
||||
this.type = type;
|
||||
wasRead = read;
|
||||
isList = false;
|
||||
}
|
||||
|
||||
public Property(String name, String[] values, Type type)
|
||||
{
|
||||
this(name, values, type, false);
|
||||
}
|
||||
|
||||
Property(String name, String[] values, Type type, boolean read)
|
||||
{
|
||||
setName(name);
|
||||
this.type = type;
|
||||
this.values = values;
|
||||
wasRead = read;
|
||||
isList = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value in this property as it's raw string.
|
||||
*
|
||||
* @return current value
|
||||
*/
|
||||
public String getString()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value in this property as an integer,
|
||||
* if the value is not a valid integer, it will return -1.
|
||||
*
|
||||
* @return The value
|
||||
*/
|
||||
public int getInt()
|
||||
{
|
||||
return getInt(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value in this property as an integer,
|
||||
* if the value is not a valid integer, it will return the
|
||||
* provided default.
|
||||
*
|
||||
* @param _default The default to provide if the current value is not a valid integer
|
||||
* @return The value
|
||||
*/
|
||||
public int getInt(int _default)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
return _default;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current value stored in this property can be converted to an integer.
|
||||
* @return True if the type of the Property is an Integer
|
||||
*/
|
||||
public boolean isIntValue()
|
||||
{
|
||||
try
|
||||
{
|
||||
Integer.parseInt(value);
|
||||
return true;
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value in this property as a boolean,
|
||||
* if the value is not a valid boolean, it will return the
|
||||
* provided default.
|
||||
*
|
||||
* @param _default The default to provide
|
||||
* @return The value as a boolean, or the default
|
||||
*/
|
||||
public boolean getBoolean(boolean _default)
|
||||
{
|
||||
if (isBooleanValue())
|
||||
{
|
||||
return Boolean.parseBoolean(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _default;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current value held by this property is a valid boolean value.
|
||||
* @return True if it is a boolean value
|
||||
*/
|
||||
public boolean isBooleanValue()
|
||||
{
|
||||
return ("true".equals(value.toLowerCase()) || "false".equals(value.toLowerCase()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current value held by this property is a valid double value.
|
||||
* @return True if the value can be converted to an double
|
||||
*/
|
||||
public boolean isDoubleValue()
|
||||
{
|
||||
try
|
||||
{
|
||||
Double.parseDouble(value);
|
||||
return true;
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value in this property as a double,
|
||||
* if the value is not a valid double, it will return the
|
||||
* provided default.
|
||||
*
|
||||
* @param _default The default to provide if the current value is not a valid double
|
||||
* @return The value
|
||||
*/
|
||||
public double getDouble(double _default)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Double.parseDouble(value);
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
return _default;
|
||||
}
|
||||
}
|
||||
|
||||
public String[] getStringList()
|
||||
{
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the integer value of all values that can
|
||||
* be parsed in the list.
|
||||
*
|
||||
* @return Array of length 0 if none of the values could be parsed.
|
||||
*/
|
||||
public int[] getIntList()
|
||||
{
|
||||
ArrayList<Integer> nums = new ArrayList<Integer>();
|
||||
|
||||
for (String value : values)
|
||||
{
|
||||
try
|
||||
{
|
||||
nums.add(Integer.parseInt(value));
|
||||
}
|
||||
catch (NumberFormatException e){}
|
||||
}
|
||||
|
||||
int[] primitives = new int[nums.size()];
|
||||
|
||||
for (int i = 0; i < nums.size(); i++)
|
||||
{
|
||||
primitives[i] = nums.get(i);
|
||||
}
|
||||
|
||||
return primitives;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if all of the current values stored in this property can be converted to an integer.
|
||||
* @return True if the type of the Property is an Integer List
|
||||
*/
|
||||
public boolean isIntList()
|
||||
{
|
||||
for (String value : values)
|
||||
{
|
||||
try
|
||||
{
|
||||
Integer.parseInt(value);
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the boolean value of all values that can
|
||||
* be parsed in the list.
|
||||
*
|
||||
* @return Array of length 0 if none of the values could be parsed.
|
||||
*/
|
||||
public boolean[] getBooleanList()
|
||||
{
|
||||
ArrayList<Boolean> tmp = new ArrayList<Boolean>();
|
||||
for (String value : values)
|
||||
{
|
||||
try
|
||||
{
|
||||
tmp.add(Boolean.parseBoolean(value));
|
||||
}
|
||||
catch (NumberFormatException e){}
|
||||
}
|
||||
|
||||
boolean[] primitives = new boolean[tmp.size()];
|
||||
|
||||
for (int i = 0; i < tmp.size(); i++)
|
||||
{
|
||||
primitives[i] = tmp.get(i);
|
||||
}
|
||||
|
||||
return primitives;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if all of current values stored in this property can be converted to a boolean.
|
||||
* @return True if it is a boolean value
|
||||
*/
|
||||
public boolean isBooleanList()
|
||||
{
|
||||
for (String value : values)
|
||||
{
|
||||
if (!"true".equalsIgnoreCase(value) && !"false".equalsIgnoreCase(value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the double value of all values that can
|
||||
* be parsed in the list.
|
||||
*
|
||||
* @return Array of length 0 if none of the values could be parsed.
|
||||
*/
|
||||
public double[] getDoubleList()
|
||||
{
|
||||
ArrayList<Double> tmp = new ArrayList<Double>();
|
||||
for (String value : values)
|
||||
{
|
||||
try
|
||||
{
|
||||
tmp.add(Double.parseDouble(value));
|
||||
}
|
||||
catch (NumberFormatException e) {}
|
||||
}
|
||||
|
||||
double[] primitives = new double[tmp.size()];
|
||||
|
||||
for (int i = 0; i < tmp.size(); i++)
|
||||
{
|
||||
primitives[i] = tmp.get(i);
|
||||
}
|
||||
|
||||
return primitives;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if all of the current values stored in this property can be converted to a double.
|
||||
* @return True if the type of the Property is a double List
|
||||
*/
|
||||
public boolean isDoubleList()
|
||||
{
|
||||
for (String value : values)
|
||||
{
|
||||
try
|
||||
{
|
||||
Double.parseDouble(value);
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this config value was just created, or if it was read from the config file.
|
||||
* This is useful for mods who auto-assign there blocks to determine if the ID returned is
|
||||
* a configured one, or a automatically generated one.
|
||||
*
|
||||
* @return True if this property was loaded from the config file with a value
|
||||
*/
|
||||
public boolean wasRead()
|
||||
{
|
||||
return wasRead;
|
||||
}
|
||||
|
||||
public Type getType()
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
public boolean isList()
|
||||
{
|
||||
return isList;
|
||||
}
|
||||
|
||||
public boolean hasChanged(){ return changed; }
|
||||
void resetChangedState(){ changed = false; }
|
||||
|
||||
public void set(String value)
|
||||
{
|
||||
this.value = value;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
public void set(String[] values)
|
||||
{
|
||||
this.values = values;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
public void set(int value){ set(Integer.toString(value)); }
|
||||
public void set(boolean value){ set(Boolean.toString(value)); }
|
||||
public void set(double value){ set(Double.toString(value)); }
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package net.minecraftforge.common;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockChest;
|
||||
import net.minecraft.block.BlockDispenser;
|
||||
import net.minecraft.block.BlockDropper;
|
||||
import net.minecraft.block.BlockPistonBase;
|
||||
import net.minecraft.world.World;
|
||||
import static net.minecraftforge.common.ForgeDirection.*;
|
||||
|
||||
public class RotationHelper {
|
||||
|
||||
private static final ForgeDirection[] UP_DOWN_AXES = new ForgeDirection[] { UP, DOWN };
|
||||
public static ForgeDirection[] getValidVanillaBlockRotations(Block block)
|
||||
{
|
||||
return block instanceof BlockChest ? UP_DOWN_AXES : VALID_DIRECTIONS;
|
||||
}
|
||||
|
||||
public static boolean rotateVanillaBlock(Block block, World worldObj, int x, int y, int z, ForgeDirection axis)
|
||||
{
|
||||
if (worldObj.isRemote)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (block instanceof BlockChest && (axis == UP || axis == DOWN))
|
||||
{
|
||||
return rotateBlock(worldObj, x, y, z, axis, 0x7);
|
||||
}
|
||||
if (block instanceof BlockPistonBase || block instanceof BlockDropper || block instanceof BlockDispenser)
|
||||
{
|
||||
return rotateBlock(worldObj, x, y, z, axis, 0x7);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean rotateBlock(World worldObj, int x, int y, int z, ForgeDirection axis, int mask)
|
||||
{
|
||||
int rotMeta = worldObj.getBlockMetadata(x, y, z);
|
||||
int masked = rotMeta & ~mask;
|
||||
ForgeDirection orientation = ForgeDirection.getOrientation(rotMeta & mask);
|
||||
ForgeDirection rotated = orientation.getRotation(axis);
|
||||
worldObj.setBlockMetadataWithNotify(x,y,z,rotated.ordinal() & mask | masked,3);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package net.minecraftforge.common;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import net.minecraft.world.chunk.storage.IChunkLoader;
|
||||
import net.minecraft.world.storage.IPlayerFileData;
|
||||
import net.minecraft.world.storage.ISaveHandler;
|
||||
import net.minecraft.world.MinecraftException;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.world.storage.WorldInfo;
|
||||
import net.minecraft.world.WorldProvider;
|
||||
import net.minecraft.world.WorldServer;
|
||||
|
||||
//Class used internally to provide the world specific data directories.
|
||||
|
||||
public class WorldSpecificSaveHandler implements ISaveHandler
|
||||
{
|
||||
private WorldServer world;
|
||||
private ISaveHandler parent;
|
||||
private File dataDir;
|
||||
|
||||
public WorldSpecificSaveHandler(WorldServer world, ISaveHandler parent)
|
||||
{
|
||||
this.world = world;
|
||||
this.parent = parent;
|
||||
dataDir = new File(world.getChunkSaveLocation(), "data");
|
||||
dataDir.mkdirs();
|
||||
}
|
||||
|
||||
@Override public WorldInfo loadWorldInfo() { return parent.loadWorldInfo(); }
|
||||
@Override public void checkSessionLock() throws MinecraftException { parent.checkSessionLock(); }
|
||||
@Override public IChunkLoader getChunkLoader(WorldProvider var1) { return parent.getChunkLoader(var1); }
|
||||
@Override public void saveWorldInfoWithPlayer(WorldInfo var1, NBTTagCompound var2) { parent.saveWorldInfoWithPlayer(var1, var2); }
|
||||
@Override public void saveWorldInfo(WorldInfo var1){ parent.saveWorldInfo(var1); }
|
||||
@Override public IPlayerFileData getSaveHandler() { return parent.getSaveHandler(); }
|
||||
@Override public void flush() { parent.flush(); }
|
||||
@Override public String getWorldDirectoryName() { return parent.getWorldDirectoryName(); }
|
||||
|
||||
@Override
|
||||
public File getMapFileFromName(String name)
|
||||
{
|
||||
return new File(dataDir, name + ".dat");
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package net.minecraftforge.common.network;
|
||||
|
||||
import net.minecraft.network.INetworkManager;
|
||||
import net.minecraft.network.NetLoginHandler;
|
||||
import net.minecraft.network.packet.NetHandler;
|
||||
import net.minecraft.network.packet.Packet1Login;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import cpw.mods.fml.common.network.IConnectionHandler;
|
||||
import cpw.mods.fml.common.network.Player;
|
||||
|
||||
public class ForgeConnectionHandler implements IConnectionHandler {
|
||||
|
||||
@Override
|
||||
public void playerLoggedIn(Player player, NetHandler netHandler, INetworkManager manager)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String connectionReceived(NetLoginHandler netHandler, INetworkManager manager)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connectionOpened(NetHandler netClientHandler, String server, int port, INetworkManager manager)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connectionOpened(NetHandler netClientHandler, MinecraftServer server, INetworkManager manager)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connectionClosed(INetworkManager manager)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clientLoggedIn(NetHandler clientHandler, INetworkManager manager, Packet1Login login)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package net.minecraftforge.common.network;
|
||||
|
||||
import net.minecraftforge.common.ForgeDummyContainer;
|
||||
import cpw.mods.fml.common.network.NetworkMod;
|
||||
import cpw.mods.fml.common.network.NetworkModHandler;
|
||||
|
||||
public class ForgeNetworkHandler extends NetworkModHandler
|
||||
{
|
||||
public ForgeNetworkHandler(ForgeDummyContainer container)
|
||||
{
|
||||
super(container,container.getClass().getAnnotation(NetworkMod.class));
|
||||
configureNetworkMod(container);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptVersion(String version)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,187 +0,0 @@
|
||||
package net.minecraftforge.common.network;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.network.INetworkManager;
|
||||
import net.minecraft.network.packet.NetHandler;
|
||||
import net.minecraft.network.packet.Packet250CustomPayload;
|
||||
import net.minecraftforge.common.network.packet.DimensionRegisterPacket;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.MapMaker;
|
||||
import com.google.common.io.ByteArrayDataInput;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.google.common.primitives.Bytes;
|
||||
import com.google.common.primitives.Ints;
|
||||
import com.google.common.primitives.UnsignedBytes;
|
||||
|
||||
import cpw.mods.fml.common.FMLLog;
|
||||
import cpw.mods.fml.common.network.FMLNetworkException;
|
||||
|
||||
public abstract class ForgePacket
|
||||
{
|
||||
public static final String CHANNEL_ID = "FORGE";
|
||||
enum Type
|
||||
{
|
||||
/**
|
||||
* Registers a dimension for a provider on client
|
||||
*/
|
||||
REGISTERDIMENSION(DimensionRegisterPacket.class);
|
||||
|
||||
private Class<? extends ForgePacket> packetType;
|
||||
private ConcurrentMap<INetworkManager, ForgePacket> partTracker;
|
||||
|
||||
private Type(Class<? extends ForgePacket> clazz)
|
||||
{
|
||||
this.packetType = clazz;
|
||||
}
|
||||
|
||||
ForgePacket make()
|
||||
{
|
||||
try
|
||||
{
|
||||
return this.packetType.newInstance();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Throwables.propagateIfPossible(e);
|
||||
FMLLog.log(Level.SEVERE, e, "A bizarre critical error occured during packet encoding");
|
||||
throw new FMLNetworkException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private ForgePacket consumePart(INetworkManager network, byte[] data)
|
||||
{
|
||||
if (partTracker == null)
|
||||
{
|
||||
partTracker = new MapMaker().weakKeys().weakValues().makeMap();
|
||||
}
|
||||
if (!partTracker.containsKey(network))
|
||||
{
|
||||
partTracker.put(network, make());
|
||||
}
|
||||
|
||||
ForgePacket pkt = partTracker.get(network);
|
||||
|
||||
ByteArrayDataInput bdi = ByteStreams.newDataInput(data);
|
||||
int chunkIdx = UnsignedBytes.toInt(bdi.readByte());
|
||||
int chunkTotal = UnsignedBytes.toInt(bdi.readByte());
|
||||
int chunkLength = bdi.readInt();
|
||||
|
||||
if (pkt.partials == null)
|
||||
{
|
||||
pkt.partials = new byte[chunkTotal][];
|
||||
}
|
||||
|
||||
pkt.partials[chunkIdx] = new byte[chunkLength];
|
||||
bdi.readFully(pkt.partials[chunkIdx]);
|
||||
for (int i = 0; i < pkt.partials.length; i++)
|
||||
{
|
||||
if (pkt.partials[i] == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return pkt;
|
||||
}
|
||||
}
|
||||
|
||||
private Type type;
|
||||
private byte[][] partials;
|
||||
|
||||
public static Packet250CustomPayload[] makePacketSet(ForgePacket packet)
|
||||
{
|
||||
byte[] packetData = packet.generatePacket();
|
||||
|
||||
if (packetData.length < 32000)
|
||||
{
|
||||
return new Packet250CustomPayload[]
|
||||
{
|
||||
new Packet250CustomPayload(CHANNEL_ID,
|
||||
Bytes.concat(new byte[]
|
||||
{
|
||||
UnsignedBytes.checkedCast(0), //IsMultipart: False
|
||||
UnsignedBytes.checkedCast(packet.getID())
|
||||
},
|
||||
packetData))
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[][] chunks = new byte[packetData.length / 32000 + 1][];
|
||||
for (int i = 0; i < packetData.length / 32000 + 1; i++)
|
||||
{
|
||||
int len = Math.min(32000, packetData.length - i* 32000);
|
||||
chunks[i] = Bytes.concat(new byte[]
|
||||
{
|
||||
UnsignedBytes.checkedCast(1), //IsMultipart: True
|
||||
UnsignedBytes.checkedCast(packet.getID()), //Packet ID
|
||||
UnsignedBytes.checkedCast(i), //Part Number
|
||||
UnsignedBytes.checkedCast(chunks.length), //Total Parts
|
||||
},
|
||||
Ints.toByteArray(len), //Length
|
||||
Arrays.copyOfRange(packetData, i * 32000, len + i * 32000));
|
||||
}
|
||||
|
||||
Packet250CustomPayload[] ret = new Packet250CustomPayload[chunks.length];
|
||||
for (int i = 0; i < chunks.length; i++)
|
||||
{
|
||||
ret[i] = new Packet250CustomPayload(CHANNEL_ID, chunks[i]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public static ForgePacket readPacket(INetworkManager network, byte[] payload)
|
||||
{
|
||||
boolean multipart = UnsignedBytes.toInt(payload[0]) == 1;
|
||||
int type = UnsignedBytes.toInt(payload[1]);
|
||||
Type eType = Type.values()[type];
|
||||
byte[] data = Arrays.copyOfRange(payload, 2, payload.length);
|
||||
|
||||
if (multipart)
|
||||
{
|
||||
ForgePacket pkt = eType.consumePart(network, data);
|
||||
if (pkt != null)
|
||||
{
|
||||
return pkt.consumePacket(Bytes.concat(pkt.partials));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return eType.make().consumePacket(data);
|
||||
}
|
||||
}
|
||||
|
||||
public ForgePacket()
|
||||
{
|
||||
for (Type t : Type.values())
|
||||
{
|
||||
if (t.packetType == getClass())
|
||||
{
|
||||
type = t;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (type == null)
|
||||
{
|
||||
throw new RuntimeException("ForgePacket constructor called on ungregistered type.");
|
||||
}
|
||||
}
|
||||
|
||||
public byte getID()
|
||||
{
|
||||
return UnsignedBytes.checkedCast(type.ordinal());
|
||||
}
|
||||
|
||||
public abstract byte[] generatePacket();
|
||||
|
||||
public abstract ForgePacket consumePacket(byte[] data);
|
||||
|
||||
public abstract void execute(INetworkManager network, EntityPlayer player);
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package net.minecraftforge.common.network;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.network.INetworkManager;
|
||||
import net.minecraft.network.NetLoginHandler;
|
||||
import net.minecraft.network.packet.Packet250CustomPayload;
|
||||
import cpw.mods.fml.common.network.FMLPacket;
|
||||
import cpw.mods.fml.common.network.IPacketHandler;
|
||||
import cpw.mods.fml.common.network.Player;
|
||||
|
||||
public class ForgePacketHandler implements IPacketHandler {
|
||||
|
||||
@Override
|
||||
public void onPacketData(INetworkManager network, Packet250CustomPayload packet, Player player)
|
||||
{
|
||||
ForgePacket pkt = ForgePacket.readPacket(network, packet.data);
|
||||
// Part of an incomplete multipart packet
|
||||
if (pkt == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pkt.execute(network, (EntityPlayer)player);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package net.minecraftforge.common.network;
|
||||
|
||||
import net.minecraft.network.packet.NetHandler;
|
||||
import net.minecraft.network.packet.Packet131MapData;
|
||||
import cpw.mods.fml.common.network.ITinyPacketHandler;
|
||||
|
||||
public class ForgeTinyPacketHandler implements ITinyPacketHandler {
|
||||
|
||||
@Override
|
||||
public void handle(NetHandler handler, Packet131MapData mapData)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/**
|
||||
* This software is provided under the terms of the Minecraft Forge Public
|
||||
* License v1.0.
|
||||
*/
|
||||
|
||||
package net.minecraftforge.common.network.packet;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.network.INetworkManager;
|
||||
import net.minecraft.network.packet.NetHandler;
|
||||
import net.minecraftforge.common.DimensionManager;
|
||||
import net.minecraftforge.common.network.ForgePacket;
|
||||
|
||||
import com.google.common.io.ByteArrayDataInput;
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
import com.google.common.io.ByteStreams;
|
||||
|
||||
/**
|
||||
* This class offers the ability for servers to register dimensions on client.
|
||||
*/
|
||||
public class DimensionRegisterPacket extends ForgePacket
|
||||
{
|
||||
|
||||
/** The dimension ID to register on client */
|
||||
public int dimensionId;
|
||||
/** The provider ID to register with dimension on client */
|
||||
public int providerId;
|
||||
|
||||
// nullary constructor required by ForgePacket.make()
|
||||
public DimensionRegisterPacket() {}
|
||||
|
||||
public DimensionRegisterPacket(int dimensionId, int providerId)
|
||||
{
|
||||
this.dimensionId = dimensionId;
|
||||
this.providerId = providerId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] generatePacket()
|
||||
{
|
||||
ByteArrayDataOutput dat = ByteStreams.newDataOutput();
|
||||
dat.writeInt(this.dimensionId);
|
||||
dat.writeInt(this.providerId);
|
||||
return dat.toByteArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForgePacket consumePacket(byte[] data)
|
||||
{
|
||||
ByteArrayDataInput dat = ByteStreams.newDataInput(data);
|
||||
dimensionId = dat.readInt();
|
||||
providerId = dat.readInt();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(INetworkManager network, EntityPlayer player)
|
||||
{
|
||||
if (!(player instanceof EntityPlayerMP))
|
||||
{
|
||||
if (!DimensionManager.isDimensionRegistered(dimensionId))
|
||||
{
|
||||
DimensionManager.registerDimension(dimensionId, providerId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
package net.minecraftforge.event;
|
||||
|
||||
import static org.objectweb.asm.Opcodes.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
|
||||
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
|
||||
public class ASMEventHandler implements IEventListener
|
||||
{
|
||||
private static int IDs = 0;
|
||||
private static final String HANDLER_DESC = Type.getInternalName(IEventListener.class);
|
||||
private static final String HANDLER_FUNC_DESC = Type.getMethodDescriptor(IEventListener.class.getDeclaredMethods()[0]);
|
||||
private static final ASMClassLoader LOADER = new ASMClassLoader();
|
||||
private static final HashMap<Method, Class<?>> cache = Maps.newHashMap();
|
||||
|
||||
private final IEventListener handler;
|
||||
private final ForgeSubscribe subInfo;
|
||||
public ASMEventHandler(Object target, Method method) throws Exception
|
||||
{
|
||||
handler = (IEventListener)createWrapper(method).getConstructor(Object.class).newInstance(target);
|
||||
subInfo = method.getAnnotation(ForgeSubscribe.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(Event event)
|
||||
{
|
||||
if (handler != null)
|
||||
{
|
||||
if (!event.isCancelable() || !event.isCanceled() || subInfo.receiveCanceled())
|
||||
{
|
||||
handler.invoke(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public EventPriority getPriority()
|
||||
{
|
||||
return subInfo.priority();
|
||||
}
|
||||
|
||||
public Class<?> createWrapper(Method callback)
|
||||
{
|
||||
if (cache.containsKey(callback))
|
||||
{
|
||||
return cache.get(callback);
|
||||
}
|
||||
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
MethodVisitor mv;
|
||||
|
||||
String name = getUniqueName(callback);
|
||||
String desc = name.replace('.', '/');
|
||||
String instType = Type.getInternalName(callback.getDeclaringClass());
|
||||
String eventType = Type.getInternalName(callback.getParameterTypes()[0]);
|
||||
|
||||
/*
|
||||
System.out.println("Name: " + name);
|
||||
System.out.println("Desc: " + desc);
|
||||
System.out.println("InstType: " + instType);
|
||||
System.out.println("Callback: " + callback.getName() + Type.getMethodDescriptor(callback));
|
||||
System.out.println("Event: " + eventType);
|
||||
*/
|
||||
|
||||
cw.visit(V1_6, ACC_PUBLIC | ACC_SUPER, desc, null, "java/lang/Object", new String[]{ HANDLER_DESC });
|
||||
|
||||
cw.visitSource(".dynamic", null);
|
||||
{
|
||||
cw.visitField(ACC_PUBLIC, "instance", "Ljava/lang/Object;", null, null).visitEnd();
|
||||
}
|
||||
{
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "(Ljava/lang/Object;)V", null, null);
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
mv.visitFieldInsn(PUTFIELD, desc, "instance", "Ljava/lang/Object;");
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(2, 2);
|
||||
mv.visitEnd();
|
||||
}
|
||||
{
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "invoke", HANDLER_FUNC_DESC, null, null);
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, desc, "instance", "Ljava/lang/Object;");
|
||||
mv.visitTypeInsn(CHECKCAST, instType);
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
mv.visitTypeInsn(CHECKCAST, eventType);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, instType, callback.getName(), Type.getMethodDescriptor(callback));
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(2, 2);
|
||||
mv.visitEnd();
|
||||
}
|
||||
cw.visitEnd();
|
||||
Class<?> ret = LOADER.define(name, cw.toByteArray());
|
||||
cache.put(callback, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private String getUniqueName(Method callback)
|
||||
{
|
||||
return String.format("%s_%d_%s_%s_%s", getClass().getName(), IDs++,
|
||||
callback.getDeclaringClass().getSimpleName(),
|
||||
callback.getName(),
|
||||
callback.getParameterTypes()[0].getSimpleName());
|
||||
}
|
||||
|
||||
private static class ASMClassLoader extends ClassLoader
|
||||
{
|
||||
private ASMClassLoader()
|
||||
{
|
||||
super(ASMClassLoader.class.getClassLoader());
|
||||
}
|
||||
|
||||
public Class<?> define(String name, byte[] data)
|
||||
{
|
||||
return defineClass(name, data, 0, data.length);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package net.minecraftforge.event;
|
||||
|
||||
import static java.lang.annotation.ElementType.TYPE;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(value = RUNTIME)
|
||||
@Target(value = TYPE)
|
||||
public @interface Cancelable{}
|
||||
@@ -1,21 +0,0 @@
|
||||
package net.minecraftforge.event;
|
||||
|
||||
import net.minecraft.command.ICommand;
|
||||
import net.minecraft.command.ICommandSender;
|
||||
|
||||
@Cancelable
|
||||
public class CommandEvent extends Event
|
||||
{
|
||||
|
||||
public final ICommand command;
|
||||
public final ICommandSender sender;
|
||||
public String[] parameters;
|
||||
public Throwable exception;
|
||||
|
||||
public CommandEvent(ICommand command, ICommandSender sender, String[] parameters)
|
||||
{
|
||||
this.command = command;
|
||||
this.sender = sender;
|
||||
this.parameters = parameters;
|
||||
}
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
package net.minecraftforge.event;
|
||||
|
||||
import static java.lang.annotation.ElementType.TYPE;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
|
||||
/**
|
||||
* Base Event class that all other events are derived from
|
||||
*/
|
||||
public class Event
|
||||
{
|
||||
@Retention(value = RUNTIME)
|
||||
@Target(value = TYPE)
|
||||
public @interface HasResult{}
|
||||
|
||||
public enum Result
|
||||
{
|
||||
DENY,
|
||||
DEFAULT,
|
||||
ALLOW
|
||||
}
|
||||
|
||||
private boolean isCanceled = false;
|
||||
private final boolean isCancelable;
|
||||
private Result result = Result.DEFAULT;
|
||||
private final boolean hasResult;
|
||||
private static ListenerList listeners = new ListenerList();
|
||||
|
||||
private static final Map<Class, Map<Class, Boolean>> annotationMap = new ConcurrentHashMap<Class, Map<Class, Boolean>>();
|
||||
|
||||
public Event()
|
||||
{
|
||||
setup();
|
||||
isCancelable = hasAnnotation(Cancelable.class);
|
||||
hasResult = hasAnnotation(HasResult.class);
|
||||
}
|
||||
|
||||
private boolean hasAnnotation(Class annotation)
|
||||
{
|
||||
Class me = this.getClass();
|
||||
Map<Class, Boolean> list = annotationMap.get(me);
|
||||
if (list == null)
|
||||
{
|
||||
list = new ConcurrentHashMap<Class, Boolean>();
|
||||
annotationMap.put(me, list);
|
||||
}
|
||||
|
||||
Boolean cached = list.get(annotation);
|
||||
if (cached != null)
|
||||
{
|
||||
return cached;
|
||||
}
|
||||
|
||||
Class cls = me;
|
||||
while (cls != Event.class)
|
||||
{
|
||||
if (cls.isAnnotationPresent(annotation))
|
||||
{
|
||||
list.put(annotation, true);
|
||||
return true;
|
||||
}
|
||||
cls = cls.getSuperclass();
|
||||
}
|
||||
|
||||
list.put(annotation, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this function is cancelable at all.
|
||||
* @return If access to setCanceled should be allowed
|
||||
*/
|
||||
public boolean isCancelable()
|
||||
{
|
||||
return isCancelable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this event is canceled and should stop executing.
|
||||
* @return The current canceled state
|
||||
*/
|
||||
public boolean isCanceled()
|
||||
{
|
||||
return isCanceled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the state of this event, not all events are cancelable, and any attempt to
|
||||
* cancel a event that can't be will result in a IllegalArgumentException.
|
||||
*
|
||||
* The functionality of setting the canceled state is defined on a per-event bases.
|
||||
*
|
||||
* @param cancel The new canceled value
|
||||
*/
|
||||
public void setCanceled(boolean cancel)
|
||||
{
|
||||
if (!isCancelable())
|
||||
{
|
||||
throw new IllegalArgumentException("Attempted to cancel a uncancelable event");
|
||||
}
|
||||
isCanceled = cancel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this event expects a significant result value.
|
||||
*/
|
||||
public boolean hasResult()
|
||||
{
|
||||
return hasResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value set as the result of this event
|
||||
*/
|
||||
public Result getResult()
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the result value for this event, not all events can have a result set, and any attempt to
|
||||
* set a result for a event that isn't expecting it will result in a IllegalArgumentException.
|
||||
*
|
||||
* The functionality of setting the result is defined on a per-event bases.
|
||||
*
|
||||
* @param value The new result
|
||||
*/
|
||||
public void setResult(Result value)
|
||||
{
|
||||
result = value;
|
||||
}
|
||||
/**
|
||||
* Called by the base constructor, this is used by ASM generated
|
||||
* event classes to setup various functionality such as the listener's list.
|
||||
*/
|
||||
protected void setup()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a ListenerList object that contains all listeners
|
||||
* that are registered to this event.
|
||||
*
|
||||
* @return Listener List
|
||||
*/
|
||||
public ListenerList getListenerList()
|
||||
{
|
||||
return listeners;
|
||||
}
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
package net.minecraftforge.event;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
|
||||
|
||||
import com.google.common.reflect.TypeToken;
|
||||
|
||||
public class EventBus
|
||||
{
|
||||
private static int maxID = 0;
|
||||
|
||||
private ConcurrentHashMap<Object, ArrayList<IEventListener>> listeners = new ConcurrentHashMap<Object, ArrayList<IEventListener>>();
|
||||
private final int busID = maxID++;
|
||||
|
||||
public EventBus()
|
||||
{
|
||||
ListenerList.resize(busID + 1);
|
||||
}
|
||||
|
||||
public void register(Object target)
|
||||
{
|
||||
if (listeners.containsKey(target))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Set<? extends Class<?>> supers = TypeToken.of(target.getClass()).getTypes().rawTypes();
|
||||
for (Method method : target.getClass().getMethods())
|
||||
{
|
||||
for (Class<?> cls : supers)
|
||||
{
|
||||
try
|
||||
{
|
||||
Method real = cls.getDeclaredMethod(method.getName(), method.getParameterTypes());
|
||||
if (real.isAnnotationPresent(ForgeSubscribe.class))
|
||||
{
|
||||
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||
if (parameterTypes.length != 1)
|
||||
{
|
||||
throw new IllegalArgumentException(
|
||||
"Method " + method + " has @ForgeSubscribe annotation, but requires " + parameterTypes.length +
|
||||
" arguments. Event handler methods must require a single argument."
|
||||
);
|
||||
}
|
||||
|
||||
Class<?> eventType = parameterTypes[0];
|
||||
|
||||
if (!Event.class.isAssignableFrom(eventType))
|
||||
{
|
||||
throw new IllegalArgumentException("Method " + method + " has @ForgeSubscribe annotation, but takes a argument that is not a Event " + eventType);
|
||||
}
|
||||
|
||||
register(eventType, target, method);
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (NoSuchMethodException e)
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void register(Class<?> eventType, Object target, Method method)
|
||||
{
|
||||
try
|
||||
{
|
||||
Constructor<?> ctr = eventType.getConstructor();
|
||||
ctr.setAccessible(true);
|
||||
Event event = (Event)ctr.newInstance();
|
||||
ASMEventHandler listener = new ASMEventHandler(target, method);
|
||||
event.getListenerList().register(busID, listener.getPriority(), listener);
|
||||
|
||||
ArrayList<IEventListener> others = listeners.get(target);
|
||||
if (others == null)
|
||||
{
|
||||
others = new ArrayList<IEventListener>();
|
||||
listeners.put(target, others);
|
||||
}
|
||||
others.add(listener);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void unregister(Object object)
|
||||
{
|
||||
ArrayList<IEventListener> list = listeners.remove(object);
|
||||
for (IEventListener listener : list)
|
||||
{
|
||||
ListenerList.unregiterAll(busID, listener);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean post(Event event)
|
||||
{
|
||||
IEventListener[] listeners = event.getListenerList().getListeners(busID);
|
||||
for (IEventListener listener : listeners)
|
||||
{
|
||||
listener.invoke(event);
|
||||
}
|
||||
return (event.isCancelable() ? event.isCanceled() : false);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package net.minecraftforge.event;
|
||||
|
||||
public enum EventPriority
|
||||
{
|
||||
/*Priority of event listeners, listeners will be sorted with respect to this priority level.
|
||||
*
|
||||
* Note:
|
||||
* Due to using a ArrayList in the ListenerList,
|
||||
* these need to stay in a contiguous index starting at 0. {Default ordinal}
|
||||
*/
|
||||
HIGHEST, //First to execute
|
||||
HIGH,
|
||||
NORMAL,
|
||||
LOW,
|
||||
LOWEST //Last to execute
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
package net.minecraftforge.event;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EnumCreatureType;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.WorldServer;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.Event.Result;
|
||||
import net.minecraftforge.event.entity.living.LivingPackSizeEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingSpawnEvent;
|
||||
import net.minecraftforge.event.entity.player.PlayerDestroyItemEvent;
|
||||
import net.minecraftforge.event.entity.player.PlayerEvent;
|
||||
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
|
||||
import net.minecraftforge.event.entity.player.PlayerInteractEvent.Action;
|
||||
import net.minecraftforge.event.world.WorldEvent;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class ForgeEventFactory
|
||||
{
|
||||
public static boolean doPlayerHarvestCheck(EntityPlayer player, Block block, boolean success)
|
||||
{
|
||||
PlayerEvent.HarvestCheck event = new PlayerEvent.HarvestCheck(player, block, success);
|
||||
MinecraftForge.EVENT_BUS.post(event);
|
||||
return event.success;
|
||||
}
|
||||
|
||||
public static float getBreakSpeed(EntityPlayer player, Block block, int metadata, float original)
|
||||
{
|
||||
PlayerEvent.BreakSpeed event = new PlayerEvent.BreakSpeed(player, block, metadata, original);
|
||||
return (MinecraftForge.EVENT_BUS.post(event) ? -1 : event.newSpeed);
|
||||
}
|
||||
|
||||
public static PlayerInteractEvent onPlayerInteract(EntityPlayer player, Action action, int x, int y, int z, int face)
|
||||
{
|
||||
PlayerInteractEvent event = new PlayerInteractEvent(player, action, x, y, z, face);
|
||||
MinecraftForge.EVENT_BUS.post(event);
|
||||
return event;
|
||||
}
|
||||
|
||||
public static void onPlayerDestroyItem(EntityPlayer player, ItemStack stack)
|
||||
{
|
||||
MinecraftForge.EVENT_BUS.post(new PlayerDestroyItemEvent(player, stack));
|
||||
}
|
||||
|
||||
public static Result canEntitySpawn(EntityLiving entity, World world, float x, float y, float z)
|
||||
{
|
||||
LivingSpawnEvent.CheckSpawn event = new LivingSpawnEvent.CheckSpawn(entity, world, x, y, z);
|
||||
MinecraftForge.EVENT_BUS.post(event);
|
||||
return event.getResult();
|
||||
}
|
||||
|
||||
public static boolean doSpecialSpawn(EntityLiving entity, World world, float x, float y, float z)
|
||||
{
|
||||
return MinecraftForge.EVENT_BUS.post(new LivingSpawnEvent.SpecialSpawn(entity, world, x, y, z));
|
||||
}
|
||||
|
||||
public static List getPotentialSpawns(WorldServer world, EnumCreatureType type, int x, int y, int z, List oldList)
|
||||
{
|
||||
WorldEvent.PotentialSpawns event = new WorldEvent.PotentialSpawns(world, type, x, y, z, oldList);
|
||||
if (MinecraftForge.EVENT_BUS.post(event))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return event.list;
|
||||
}
|
||||
|
||||
public static int getMaxSpawnPackSize(EntityLiving entity)
|
||||
{
|
||||
LivingPackSizeEvent maxCanSpawnEvent = new LivingPackSizeEvent(entity);
|
||||
MinecraftForge.EVENT_BUS.post(maxCanSpawnEvent);
|
||||
return maxCanSpawnEvent.getResult() == Result.ALLOW ? maxCanSpawnEvent.maxPackSize : entity.getMaxSpawnedInChunk();
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package net.minecraftforge.event;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static java.lang.annotation.RetentionPolicy.*;
|
||||
import static java.lang.annotation.ElementType.*;
|
||||
|
||||
@Retention(value = RUNTIME)
|
||||
@Target(value = METHOD)
|
||||
public @interface ForgeSubscribe
|
||||
{
|
||||
public EventPriority priority() default EventPriority.NORMAL;
|
||||
public boolean receiveCanceled() default false;
|
||||
}
|
||||
Loaded 100 of 524 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user