diff --git a/src/mc.erl b/src/mc.erl index 1cb0f25..559174f 100644 --- a/src/mc.erl +++ b/src/mc.erl @@ -191,6 +191,17 @@ handle_packet(Client, {kick, Message}) -> gen_server:cast(Client, {kick, Message}), none; +handle_packet(_Client, {arm_animation, _EntityID, _Unknown}) -> + none; + +handle_packet(Client, {block_dig, Stage, X, Y, Z, Direction}) -> + case mc_world:block_dig(Stage, X, Y, Z, Direction, Client) of + {block_change, X, Y, Z, Type, Meta} -> + mc_reply:block_change(X, Y, Z, Type, Meta); + _ -> + none + end; + handle_packet(_State, Unknown) -> io:format("Unknown Packet: ~p~n", [Unknown]), <<"">>. diff --git a/src/mc_reply.erl b/src/mc_reply.erl index c2ce6a6..9ee35e9 100644 --- a/src/mc_reply.erl +++ b/src/mc_reply.erl @@ -31,8 +31,12 @@ entity_add_mob(EntityID, Type, X, Y, Z, R, P) -> chat(Message) -> mc_util:write_packet(16#03, [{string, Message}]). +block_change(X, Y, Z, Type, Meta) -> + mc_util:write_packet(16#35, [{int, X}, {byte,Y}, {int, Z}, {byte, Type}, {byte, Meta}]). -fake_world(Pid, BlockCount, LocX, LocY, LocZ) -> + + +fake_world(Pid, BlockCount, LocX, _LocY, LocZ) -> Width = math:sqrt(BlockCount), erlcraft_client_fsm:send_packet(Pid, mc_reply:chat("Hello world")), lists:map( diff --git a/src/mc_world.erl b/src/mc_world.erl index 3610fc3..5c5ef48 100644 --- a/src/mc_world.erl +++ b/src/mc_world.erl @@ -4,20 +4,77 @@ -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). --record(state, {world_path, world}). +-record(state, {world_path, world, chunk_store}). %% External API --export([start_link/1, get_chunk/3, get_spawn/0, load_chunk/4, dbg_chunk/3]). +-export([start_link/1, get_chunk/3, get_spawn/0, load_chunk/5, dbg_chunk/3, block_dig/6]). generate_pre_chunk(X,Z, Update) -> mc_util:write_packet(16#32, [{int, X}, {int, Z}, {bool, Update}]). +block_from_bin(Binary, Offset) -> + <<_:Offset/binary, Block:8/integer, _/binary>> = Binary, + Block. +block_from_bin_packed(Binary, Offset) -> + AdjOffset = Offset * 4, + <<_:AdjOffset/bits, Block:8/integer, _/bits>> = Binary, + Block bsr 4. + %generate_chunk(X, Y, Z, SizeX, SizeY, SizeZ) -> % Data = mc_util:chunk_data(SizeX * SizeY * SizeX), % Compressed = zlib:compress(mc_util:encode_list(Data)), % mc_util:write_packet(16#33, lists:flatten([{int, X*16}, {short, Y}, {int, Z*16}, {byte, SizeX-1}, {byte, SizeY-1}, {byte, SizeZ-1}, {int, size(Compressed)}, {binary, Compressed}])). +block_dig(State, _X, _Y, _Z, _Direction, _Client, _ChunkStore) when State =:= 0 -> + ok; +block_dig(State, _X, _Y, _Z, _Direction, _Client, _ChunkStore) when State =:= 1 -> + ok; +block_dig(State, _X, _Y, _Z, _Direction, _Client, _ChunkStore) when State =:= 2 -> + ok; +block_dig(State, X, Y, Z, Direction, _Client, ChunkStore) when State =:= 3 -> + io:format("Dig: S: ~p [~p,~p,~p], D: ~p~n", [State, X, Y, Z, Direction]), + Key = {X, Z}, + case ets:lookup(ChunkStore, Key) of + [] -> + io:format("Key: ~p not found[~p,~p,~p]~n", [Key, X, Y, Z]); + [{Key, BD, MD, WL}] -> + Offset = Y, + HalfOffset = trunc(Y/2), + Block = block_from_bin(BD, Offset), + Meta = block_from_bin_packed(MD, HalfOffset), + io:format("B: ~p M: ~p~n", [Block, Meta]), + <> = BD, + NBD = <>, + ets:insert(ChunkStore, {Key, NBD, MD, WL}), + {block_change, X, Y, Z, 0, 8}; + Else -> + io:format("unknown: ~p key: ~p~n", [Else, Key]), + ok + end. + +load_chunk(X, Y, Z, Root, ChunkStore) -> + SizeX = 16, SizeY = 128, SizeZ = 16, + [BlockData, MetaData, LightData] = try ets:member(ChunkStore, {X*16, Z*16}) of + true -> load_chunk_ets(X, Y, Z, ChunkStore); + false-> load_chunk_disk(X, Y, Z, Root, ChunkStore) + catch _:_ -> + load_chunk_disk(X, Y, Z, Root, ChunkStore) + end, + Compressed = zlib:compress(<>), + mc_util:write_packet(16#33, lists:flatten([{int, X*16}, {short, Y}, {int, Z*16}, {byte, SizeX-1}, {byte, SizeY-1}, {byte, SizeZ-1}, {int, size(Compressed)}, {binary, Compressed}])). -load_chunk(X,Y,Z, Root) -> + +load_chunk_ets(X, _Y, Z, ChunkStore) -> + lists:foldl(fun(Idx, [Blocks, Meta, Light]) -> + OZ = Idx rem 16, + OX = trunc(Idx/16), + case ets:lookup(ChunkStore, {X * 16 + OX, Z * 16 + OZ}) of + [{{_,_}, BD, MD, WL}] -> + [<>, <>, <>]; + _ -> io:format("Could not find key: [~p,~p]", [X*16 + OX, Z*16 + OZ]) + end + end, [<<>>, <<>>, <<>>], lists:seq(0, 255)). + +load_chunk_disk(X, _Y, Z, Root, ChunkStore) -> F1 = string:to_lower(case X of PosX when PosX >= 0 -> erlang:integer_to_list(X rem 64, 36); @@ -38,11 +95,19 @@ load_chunk(X,Y,Z, Root) -> {tag_byte_array, <<"BlockLight">>, BlockLight} = lists:keyfind(<<"BlockLight">>, 2, LevelData), {tag_byte_array, <<"SkyLight">>, SkyLight} = lists:keyfind(<<"SkyLight">>, 2, LevelData), {tag_byte_array, <<"Data">>, MetaData} = lists:keyfind(<<"Data">>, 2, LevelData), - SizeX = 16, SizeY = 128, SizeZ = 16, - %MetaInfo = mc_util:expand_4_to_8(MetaData), WorldLight = mc_util:or_binaries(BlockLight, SkyLight), - Compressed = zlib:compress(<>), - mc_util:write_packet(16#33, lists:flatten([{int, X*16}, {short, Y}, {int, Z*16}, {byte, SizeX-1}, {byte, SizeY-1}, {byte, SizeZ-1}, {int, size(Compressed)}, {binary, Compressed}])). + lists:foreach(fun(Idx) -> + BlockIdx = Idx * 128, + MetaIdx = Idx * 64, + OZ = Idx rem 16, + OX = trunc(Idx/16), + <<_:BlockIdx/binary, BD:128/binary, _/binary>> = Blocks, + <<_:MetaIdx/binary, MD:64/binary, _/binary>> = MetaData, + <<_:MetaIdx/binary, WL:64/binary, _/binary>> = WorldLight, + true = ets:insert(ChunkStore, {{X * 16 + OX,Z * 16 + OZ}, BD, MD, WL}) + end, lists:seq(0, 255)), + io:format("Wrote [~p,~p] to [~p, ~p]~n", [X*16, Z*16, X*16+16, Z*16+16]), + [Blocks, MetaData, WorldLight]. dbg_chunk(<<>>, <<>>, <<>>) -> ok; @@ -64,6 +129,9 @@ dbg_chunk(Blocks, MetaData, LightData) -> end, dbg_chunk(RestBlocks, RestMeta, RestLight). +block_dig(Stage, X, Y, Z, Direction, Client) -> + gen_server:call(?MODULE, {block_dig, Stage, X, Y, Z, Direction, Client}). + get_spawn() -> gen_server:call(?MODULE, {get_spawn}). @@ -82,13 +150,14 @@ init([MapName]) -> Path = string:join([Cwd, MapName] , "/"), LevelPath = string:join([Path, "level.dat"], "/"), World = nbt:load_file(LevelPath), - {ok, #state{world_path = Path, world = World}}. + ChunkStore = ets:new(world, []), + {ok, #state{world_path = Path, world = World, chunk_store = ChunkStore}}. -handle_call({get_chunk, X, Y, Z}, _From, #state{world_path = WorldPath} = State) when is_integer(X), is_integer(Z) -> +handle_call({get_chunk, X, Y, Z}, _From, #state{world_path = WorldPath, chunk_store = ChunkStore} = State) when is_integer(X), is_integer(Z) -> io:format("Chunk Request: [~p, ~p, ~p]~n", [X, Y, Z]), PreChunk = generate_pre_chunk(X, Z, 1), % ChunkData = generate_chunk(X, Y, Z, 16, 128, 16), - ChunkData = load_chunk(X, Y, Z, WorldPath), + ChunkData = load_chunk(X, Y, Z, WorldPath, ChunkStore), {reply, {chunk, PreChunk, ChunkData}, State}; handle_call({get_spawn}, _From, #state{world = World} = State) -> @@ -114,7 +183,11 @@ handle_call({get_spawn}, _From, #state{world = World} = State) -> end, io:format("Spawning player at: [~p, ~p, ~p]~n", [SpawnX, SpawnY, SpawnZ]), {reply, {loc, SX, SY, SZ, SY - 1.5, 0.0, 0.0}, State}; - + +handle_call({block_dig, Stage, X, Y, Z, Direction, Client}, _From, #state{chunk_store = ChunkStore} = _State) -> + BlockChange = block_dig(Stage, X, Y, Z, Direction, Client, ChunkStore), + {reply, BlockChange, _State}; + handle_call(_Request, _From, _State) -> io:format("Call: ~p~n", [_Request]), {reply, none, _State}.