Add SoundPatcher and refactor ResourceFetcher, in preparation for fetching sound/music resources.

This commit is contained in:
UnknownShadow200 2015-11-27 07:22:29 +11:00
parent b0dd9d0651
commit b1a972a905
13 changed files with 445 additions and 18 deletions

View File

@ -71,6 +71,9 @@
</PropertyGroup> </PropertyGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" />
<ItemGroup> <ItemGroup>
<Reference Include="SharpWave">
<HintPath>SharpWave.dll</HintPath>
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Drawing" /> <Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" /> <Reference Include="System.Windows.Forms" />
@ -243,6 +246,9 @@
<None Include="..\readme.md"> <None Include="..\readme.md">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Include="SharpWave.dll.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="2D\Drawing" /> <Folder Include="2D\Drawing" />

View File

@ -0,0 +1,66 @@
using System;
using ClassicalSharp.Entities;
using OpenTK;
namespace ClassicalSharp.Particles {
public abstract class CollidableParticle : Particle {
const float gravity = 3.4f;
//const float gravity = 0.7f; // TODO: temp debug
public CollidableParticle( Game game, Vector3 pos, Vector3 velocity, double lifetime )
: base( game, pos, velocity, lifetime ) {
}
public override bool Tick( double delta ) {
lastPos = Position = nextPos;
byte curBlock = game.World.SafeGetBlock( (int)Position.X, (int)Position.Y, (int)Position.Z );
if( !CanPassThrough( curBlock ) ) return true;
Velocity.Y -= gravity * (float)delta;
int startY = (int)Math.Floor( Position.Y );
Position += Velocity * (float)delta * 3;
int endY = (int)Math.Floor( Position.Y );
Utils.Clamp( ref Position.X, 0, game.World.Width - 0.01f );
Utils.Clamp( ref Position.Z, 0, game.World.Length - 0.01f );
if( Velocity.Y > 0 ) {
for( int y = startY; y <= endY && TestY( y, false ); y++ );
} else {
for( int y = startY; y >= endY && TestY( y, true ); y-- );
}
nextPos = Position;
Position = lastPos;
return base.Tick( delta );
}
bool TestY( int y, bool topFace ) {
if( y < 0 ) {
Position.Y = nextPos.Y = lastPos.Y = 0 + Entity.Adjustment;
Velocity = Vector3.Zero;
return false;
}
byte block = game.World.SafeGetBlock( (int)Position.X, y, (int)Position.Z );
if( CanPassThrough( block ) ) return true;
float collideY = y;
if( topFace )
collideY += game.BlockInfo.Height[block];
bool collide = topFace ? (Position.Y < collideY) : (Position.Y > collideY );
if( collide ) {
float adjust = topFace ? Entity.Adjustment : -Entity.Adjustment;
Position.Y = nextPos.Y = lastPos.Y = collideY + adjust;
Velocity = Vector3.Zero;
return false;
}
return true;
}
bool CanPassThrough( byte block ) {
return block == 0 || game.BlockInfo.IsSprite[block] || game.BlockInfo.IsLiquid[block];
}
}
}

View File

@ -0,0 +1,28 @@
using System;
using OpenTK;
namespace ClassicalSharp.Particles {
public abstract class Particle {
public Vector3 Position;
public Vector3 Velocity;
public float Lifetime;
protected Game game;
protected Vector3 lastPos, nextPos;
public abstract void Render( double delta, float t, VertexPos3fTex2fCol4b[] vertices, ref int index );
public Particle( Game game, Vector3 pos, Vector3 velocity, double lifetime ) {
this.game = game;
Position = lastPos = nextPos = pos;
Velocity = velocity;
Lifetime = (float)lifetime;
}
public virtual bool Tick( double delta ) {
Lifetime -= (float)delta;
return Lifetime < 0;
}
}
}

View File

@ -0,0 +1,129 @@
using System;
using System.Collections.Generic;
using ClassicalSharp.GraphicsAPI;
using OpenTK;
namespace ClassicalSharp.Particles {
public class ParticleManager : IDisposable {
public int ParticlesTexId;
List<Particle> terrainParticles = new List<Particle>();
List<Particle> rainParticles = new List<Particle>();
Game game;
Random rnd;
int vb;
public ParticleManager( Game game ) {
this.game = game;
rnd = new Random();
vb = game.Graphics.CreateDynamicVb( VertexFormat.Pos3fTex2fCol4b, 1000 );
}
VertexPos3fTex2fCol4b[] vertices = new VertexPos3fTex2fCol4b[0];
public void Render( double delta, float t ) {
if( terrainParticles.Count == 0 && rainParticles.Count == 0 ) return;
IGraphicsApi graphics = game.Graphics;
graphics.Texturing = true;
graphics.AlphaTest = true;
graphics.SetBatchFormat( VertexFormat.Pos3fTex2fCol4b );
int count = RenderParticles( terrainParticles, delta, t );
if( count > 0 ) {
graphics.BindTexture( game.TerrainAtlas.TexId );
graphics.UpdateDynamicIndexedVb( DrawMode.Triangles, vb, vertices, count, count * 6 / 4 );
}
count = RenderParticles( rainParticles, delta, t );
if( count > 0 ) {
graphics.BindTexture( ParticlesTexId );
graphics.UpdateDynamicIndexedVb( DrawMode.Triangles, vb, vertices, count, count * 6 / 4 );
}
graphics.AlphaTest = false;
graphics.Texturing = false;
}
int RenderParticles( List<Particle> particles, double delta, float t ) {
int count = particles.Count * 4;
if( count > vertices.Length )
vertices = new VertexPos3fTex2fCol4b[count];
int index = 0;
for( int i = 0; i < particles.Count; i++ )
particles[i].Render( delta, t, vertices, ref index );
return Math.Min( count, 1000 );
}
public void Tick( double delta ) {
TickParticles( terrainParticles, delta );
TickParticles( rainParticles, delta );
}
void TickParticles( List<Particle> particles, double delta ) {
for( int i = 0; i < particles.Count; i++ ) {
Particle particle = particles[i];
if( particle.Tick( delta ) ) {
particles.RemoveAt( i );
i--;
}
}
}
public void Dispose() {
game.Graphics.DeleteDynamicVb( vb );
game.Graphics.DeleteTexture( ref ParticlesTexId );
}
public void BreakBlockEffect( Vector3I position, byte block ) {
Vector3 startPos = new Vector3( position.X, position.Y, position.Z );
int texLoc = game.BlockInfo.GetTextureLoc( block, TileSide.Left );
TextureRec rec = game.TerrainAtlas.GetTexRec( texLoc );
const float invSize = TerrainAtlas2D.invElementSize;
const int cellsCount = (int)((1/4f) / invSize);
const float elemSize = invSize / 4f;
float blockHeight = game.BlockInfo.Height[block];
for( int i = 0; i < 25; i++ ) {
double velX = rnd.NextDouble() * 0.8 - 0.4; // [-0.4, 0.4]
double velZ = rnd.NextDouble() * 0.8 - 0.4;
double velY = rnd.NextDouble() + 0.2;
Vector3 velocity = new Vector3( (float)velX, (float)velY, (float)velZ );
double xOffset = rnd.NextDouble() - 0.5; // [-0.5, 0.5]
double yOffset = (rnd.NextDouble() - 0.125) * blockHeight;
double zOffset = rnd.NextDouble() - 0.5;
Vector3 pos = startPos + new Vector3( 0.5f + (float)xOffset,
(float)yOffset, 0.5f + (float)zOffset );
TextureRec particleRec = rec;
particleRec.U1 = rec.U1 + rnd.Next( 0, cellsCount ) * elemSize;
particleRec.V1 = rec.V1 + rnd.Next( 0, cellsCount ) * elemSize;
particleRec.U2 = particleRec.U1 + elemSize;
particleRec.V2 = particleRec.V1 + elemSize;
double life = 1.5 - rnd.NextDouble();
terrainParticles.Add( new TerrainParticle( game, pos, velocity, life, particleRec ) );
}
}
public void AddRainParticle( Vector3 pos ) {
Vector3 startPos = pos;
for( int i = 0; i < 5; i++ ) {
double velX = rnd.NextDouble() * 0.8 - 0.4; // [-0.4, 0.4]
double velZ = rnd.NextDouble() * 0.8 - 0.4;
double velY = rnd.NextDouble() + 0.5;
Vector3 velocity = new Vector3( (float)velX, (float)velY, (float)velZ );
double xOffset = rnd.NextDouble() - 0.5; // [-0.5, 0.5]
double yOffset = 0.01;
double zOffset = rnd.NextDouble() - 0.5;
pos = startPos + new Vector3( 0.5f + (float)xOffset,
(float)yOffset, 0.5f + (float)zOffset );
double life = 3.5 - rnd.NextDouble();
rainParticles.Add( new RainParticle( game, pos, velocity, life ) );
}
}
}
}

View File

@ -0,0 +1,28 @@
using System;
using OpenTK;
namespace ClassicalSharp.Particles {
public sealed class RainParticle : CollidableParticle {
static Vector2 rainSize = new Vector2( 1/8f, 1/8f );
static TextureRec rec = new TextureRec( 2/128f, 14/128f, 3/128f, 2/128f );
public RainParticle( Game game, Vector3 pos, Vector3 velocity, double lifetime )
: base( game, pos, velocity, lifetime ) {
}
public override void Render( double delta, float t, VertexPos3fTex2fCol4b[] vertices, ref int index ) {
Position = Vector3.Lerp( lastPos, nextPos, t );
Vector3 p111, p121, p212, p222;
Utils.CalcBillboardPoints( rainSize, Position, ref game.View,
out p111, out p121, out p212, out p222 );
World map = game.World;
FastColour col = map.IsLit( Vector3I.Floor( Position ) ) ? map.Sunlight : map.Shadowlight;
vertices[index++] = new VertexPos3fTex2fCol4b( p111, rec.U1, rec.V2, col );
vertices[index++] = new VertexPos3fTex2fCol4b( p121, rec.U1, rec.V1, col );
vertices[index++] = new VertexPos3fTex2fCol4b( p222, rec.U2, rec.V1, col );
vertices[index++] = new VertexPos3fTex2fCol4b( p212, rec.U2, rec.V2, col );
}
}
}

View File

@ -0,0 +1,29 @@
using System;
using OpenTK;
namespace ClassicalSharp.Particles {
public sealed class TerrainParticle : CollidableParticle {
static Vector2 terrainSize = new Vector2( 1/8f, 1/8f );
TextureRec rec;
public TerrainParticle( Game game, Vector3 pos, Vector3 velocity, double lifetime, TextureRec rec )
: base( game, pos, velocity, lifetime ) {
this.rec = rec;
}
public override void Render( double delta, float t, VertexPos3fTex2fCol4b[] vertices, ref int index ) {
Position = Vector3.Lerp( lastPos, nextPos, t );
Vector3 p111, p121, p212, p222;
Utils.CalcBillboardPoints( terrainSize, Position, ref game.View,
out p111, out p121, out p212, out p222 );
World map = game.World;
FastColour col = map.IsLit( Vector3I.Floor( Position ) ) ? map.Sunlight : map.Shadowlight;
vertices[index++] = new VertexPos3fTex2fCol4b( p111, rec.U1, rec.V2, col );
vertices[index++] = new VertexPos3fTex2fCol4b( p121, rec.U1, rec.V1, col );
vertices[index++] = new VertexPos3fTex2fCol4b( p222, rec.U2, rec.V1, col );
vertices[index++] = new VertexPos3fTex2fCol4b( p212, rec.U2, rec.V2, col );
}
}
}

Binary file not shown.

View File

@ -0,0 +1,4 @@
<configuration>
<dllmap os="linux" dll="openal32.dll" target="libopenal.so.1"/>
<dllmap os="osx" dll="openal32.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
</configuration>

View File

@ -48,10 +48,9 @@ namespace Launcher2 {
game.Downloader = new AsyncDownloader( "null" ); game.Downloader = new AsyncDownloader( "null" );
if( fetcher != null ) return; if( fetcher != null ) return;
fetcher = new ResourceFetcher( game.Downloader ); fetcher = game.fetcher;
fetcher.DownloadItems( SetStatus ); fetcher.DownloadItems( game.Downloader, SetStatus );
selectedWidget = null; selectedWidget = null;
Resize(); Resize();
} }
@ -59,7 +58,7 @@ namespace Launcher2 {
static FastColour backCol = new FastColour( 120, 85, 151 ); static FastColour backCol = new FastColour( 120, 85, 151 );
static readonly string mainText = "Some required resources weren't found" + static readonly string mainText = "Some required resources weren't found" +
Environment.NewLine + "Okay to download them?"; Environment.NewLine + "Okay to download them?";
static readonly string format = "Estimated size: {0} megabytes"; static readonly string format = "Download size: {0} megabytes";
static FastColour clearCol = new FastColour( 12, 12, 12 ); static FastColour clearCol = new FastColour( 12, 12, 12 );
void Draw() { void Draw() {
@ -68,8 +67,9 @@ namespace Launcher2 {
drawer.DrawRect( backCol, game.Width / 2 - 175, drawer.DrawRect( backCol, game.Width / 2 - 175,
game.Height / 2 - 70, 175 * 2, 70 * 2 ); game.Height / 2 - 70, 175 * 2, 70 * 2 );
float dataSize = game.fetcher.DownloadSize;
string text = widgets[0] != null ? widgets[0].Text string text = widgets[0] != null ? widgets[0].Text
: String.Format( format, ResourceFetcher.EstimateDownloadSize() ); : String.Format( format, dataSize.ToString( "F2" ) );
MakeLabelAt( text, statusFont, Anchor.Centre, Anchor.Centre, 0, 5 ); MakeLabelAt( text, statusFont, Anchor.Centre, Anchor.Centre, 0, 5 );
// Clear the entire previous widgets state. // Clear the entire previous widgets state.

View File

@ -45,6 +45,9 @@
<BaseIntermediateOutputPath>obj\</BaseIntermediateOutputPath> <BaseIntermediateOutputPath>obj\</BaseIntermediateOutputPath>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="SharpWave">
<HintPath>..\ClassicalSharp\SharpWave.dll</HintPath>
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Drawing" /> <Reference Include="System.Drawing" />
<Reference Include="System.Security" /> <Reference Include="System.Security" />
@ -72,6 +75,7 @@
<Compile Include="Patcher\Animations.cs" /> <Compile Include="Patcher\Animations.cs" />
<Compile Include="Patcher\ResourceFetcher.cs" /> <Compile Include="Patcher\ResourceFetcher.cs" />
<Compile Include="Patcher\ResourcePatcher.cs" /> <Compile Include="Patcher\ResourcePatcher.cs" />
<Compile Include="Patcher\SoundPatcher.cs" />
<Compile Include="Patcher\ZipWriter.cs" /> <Compile Include="Patcher\ZipWriter.cs" />
<Compile Include="Program.cs" /> <Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />

View File

@ -48,6 +48,8 @@ namespace Launcher2 {
public Dictionary<string, Dictionary<string, object>> ScreenMetadata = public Dictionary<string, Dictionary<string, object>> ScreenMetadata =
new Dictionary<string, Dictionary<string, object>>(); new Dictionary<string, Dictionary<string, object>>();
internal ResourceFetcher fetcher;
Font logoFont, logoItalicFont; Font logoFont, logoItalicFont;
PlatformDrawer platformDrawer; PlatformDrawer platformDrawer;
public void Init() { public void Init() {
@ -120,7 +122,9 @@ namespace Launcher2 {
TryLoadTexturePack(); TryLoadTexturePack();
platformDrawer.Init( Window.WindowInfo ); platformDrawer.Init( Window.WindowInfo );
if( !ResourceFetcher.CheckAllResourcesExist() ) { fetcher = new ResourceFetcher();
fetcher.CheckResourceExistence();
if( !fetcher.AllResourcesExist ) {
SetScreen( new ResourcesScreen( this ) ); SetScreen( new ResourcesScreen( this ) );
} else { } else {
SetScreen( new MainScreen( this ) ); SetScreen( new MainScreen( this ) );

View File

@ -7,17 +7,22 @@ namespace Launcher2 {
public sealed class ResourceFetcher { public sealed class ResourceFetcher {
public bool Done = false; public bool Done = false;
AsyncDownloader downloader; internal AsyncDownloader downloader;
public ResourceFetcher( AsyncDownloader downloader ) { public ResourceFetcher() {
this.downloader = downloader; digPath = Path.Combine( "audio", "dig" );
stepPath = Path.Combine( "audio", "step" );
} }
const string jarClassicUri = "http://s3.amazonaws.com/Minecraft.Download/versions/c0.30_01c/c0.30_01c.jar"; const string jarClassicUri = "http://s3.amazonaws.com/Minecraft.Download/versions/c0.30_01c/c0.30_01c.jar";
const string jar162Uri = "http://s3.amazonaws.com/Minecraft.Download/versions/1.6.2/1.6.2.jar"; const string jar162Uri = "http://s3.amazonaws.com/Minecraft.Download/versions/1.6.2/1.6.2.jar";
const string pngTerrainPatchUri = "http://static.classicube.net/terrain-patch.png"; const string pngTerrainPatchUri = "http://static.classicube.net/terrain-patch.png";
const string pngGuiPatchUri = "http://static.classicube.net/gui.png"; const string pngGuiPatchUri = "http://static.classicube.net/gui.png";
const string digSoundsUri = "http://s3.amazonaws.com/MinecraftResources/sound3/dig/";
const string stepSoundsUri = "http://s3.amazonaws.com/MinecraftResources/sound3/step/";
const string musicUri = "http://s3.amazonaws.com/MinecraftResources/music/";
public void DownloadItems( Action<string> setStatus ) { public void DownloadItems( AsyncDownloader downloader, Action<string> setStatus ) {
this.downloader = downloader;
downloader.DownloadData( jarClassicUri, false, "classic_jar" ); downloader.DownloadData( jarClassicUri, false, "classic_jar" );
downloader.DownloadData( jar162Uri, false, "162_jar" ); downloader.DownloadData( jar162Uri, false, "162_jar" );
downloader.DownloadData( pngTerrainPatchUri, false, "terrain_patch" ); downloader.DownloadData( pngTerrainPatchUri, false, "terrain_patch" );
@ -69,14 +74,50 @@ namespace Launcher2 {
return true; return true;
} }
public static bool CheckAllResourcesExist() { public void CheckResourceExistence() {
return File.Exists( "default.zip" ); AllResourcesExist = File.Exists( "default.zip" );
//&& Directory.Exists( "audio" ) && File.Exists( digPath + ".bin" )
//&& File.Exists( stepPath + ".bin" );
if( !File.Exists( "default.zip" ) ) {
// classic.jar + 1.6.2.jar + terrain-patch.png + gui.png
DownloadSize += (291 + 4621 + 7 + 21) / 1024f;
ResourcesCount += 4;
} }
public static string EstimateDownloadSize() { for( int i = 0; i < musicFiles.Length; i++ ) {
float size = (291 + 4621 + 7 + 21) / 1024f; string file = Path.Combine( "audio", musicFiles[i] + ".ogg" );
// classic.jar + 1.6.2.jar + terrain-patch.png + gui.png musicExists[i] = File.Exists( file );
return size.ToString( "F2" ); continue;
// TODO: download music files
if( !musicExists[i] ) {
DownloadSize += musicSizes[i] / 1024f;
ResourcesCount++;
} }
} }
ResourcesCount += digSounds.Length;
ResourcesCount += stepSounds.Length;
}
public bool AllResourcesExist;
public float DownloadSize;
public int ResourcesCount;
string digPath, stepPath;
string[] digSounds = new [] { "cloth1", "cloth2", "cloth3", "cloth4",
"glass1", "glass2", "glass3", "glass4", "grass1", "grass2", "grass3",
"grass4", "gravel1", "gravel2", "gravel3", "gravel4", "sand1", "sand2",
"sand3", "sand4", "snow1", "snow2", "snow3", "snow4", "stone1", "stone2",
"stone3", "stone4", "wood1", "wood2", "wood3", "wood4" };
string[] stepSounds = new [] { "cloth1", "cloth2", "cloth3", "cloth4", "grass1",
"grass2", "grass3", "grass4", "grass5", "grass6", "gravel1", "gravel2",
"gravel3", "gravel4", "sand1", "sand2", "sand3", "sand4", "sand5", "snow1",
"snow2", "snow3", "snow4", "stone1", "stone2", "stone3", "stone4", "stone5",
"stone6", "wood1", "wood2", "wood3", "wood4", "wood5", "wood6" };
string[] musicFiles = new [] { "calm1", "calm2",
"calm3", "hal1", "hal2", "hal3" };
int[] musicSizes = new [] { 2472, 1931, 2181, 1926, 1714, 1879, 2499 };
bool[] musicExists = new bool[6];
}
} }

View File

@ -0,0 +1,88 @@
using System;
using System.IO;
using ClassicalSharp.Network;
using SharpWave;
using SharpWave.Codecs;
using SharpWave.Codecs.Vorbis;
using SharpWave.Containers;
namespace Launcher2 {
public sealed class SoundPatcher {
string[] files, identifiers;
string prefix;
FileStream outData;
StreamWriter outText;
RawOut outDecoder;
public SoundPatcher( string[] files, string prefix, string outputPath ) {
this.files = files;
this.prefix = prefix;
InitOutput( outputPath );
}
public void FetchFiles( string baseUrl, ResourceFetcher fetcher ) {
identifiers = new string[files.Length];
for( int i = 0; i < files.Length; i++ )
identifiers[i] = prefix + files[i];
for( int i = 0; i < files.Length; i++ ) {
string url = baseUrl + files[i] + ".ogg";
fetcher.downloader.DownloadData( url, false, identifiers[i] );
}
}
public bool CheckDownloaded( ResourceFetcher fetcher, Action<string> setStatus ) {
for( int i = 0; i < identifiers.Length; i++ ) {
DownloadedItem item;
if( fetcher.downloader.TryGetItem( identifiers[i], out item ) ) {
Console.WriteLine( "found sound " + identifiers[i] );
if( item.Data == null ) {
setStatus( "&cFailed to download " + identifiers[i] );
return false;
}
DecodeSound( files[i], (byte[])item.Data );
// TODO: setStatus( next );
if( i == identifiers.Length - 1 ) {
Dispose();
}
}
}
return true;
}
void DecodeSound( string name, byte[] rawData ) {
long start = outData.Position;
using( MemoryStream ms = new MemoryStream( rawData ) ) {
OggContainer container = new OggContainer( ms );
outDecoder.PlayStreaming( container );
}
long len = outData.Position - start;
outText.WriteLine( format, name, outDecoder.Frequency,
outDecoder.BitsPerSample, outDecoder.Channels,
start, len );
}
const string format = "{0},{1},{2},{3},{4},{5}";
void InitOutput( string outputPath ) {
outData = File.Create( outputPath + ".bin" );
outText = new StreamWriter( outputPath + ".txt" );
outDecoder = new RawOut( outData, true );
outText.WriteLine( "# This file indicates where the various raw decompressed sound data " +
"are found in the corresponding raw .bin file." );
outText.WriteLine( "# Each line is in the following format:" );
outText.WriteLine( "# Identifier, Frequency/Sample rate, BitsPerSample, " +
"Channels, Offset from start, Length in bytes" );
}
void Dispose() {
outDecoder.Dispose();
outData.Close();
outText.Close();
}
}
}