Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

client can now break blocks, and pick up the fallen pieces

  • Loading branch information...
commit ed336eb8da4d83e937687ce34feecb76b4128288 1 parent 38d5cd2
@ScottBrooks authored
View
17 src/erlcraft_client.erl
@@ -24,9 +24,11 @@ update_chunks(ChunkX, ChunkY, ChunkZ, FSMPid, ChunkList) ->
ChunksToLoad = lists:filter(fun(Key) -> sets:is_element(Key, ChunkList) =:= false end, Keys),
lists:foldl(fun(Key, Acc) ->
{pos, CX, CY, CZ} = Key,
+ io:format("Asking for Chunk at: ~p~n", [Key]),
{chunk, PreChunk, Chunk} = mc_world:get_chunk(CX, CY, CZ),
erlcraft_client_fsm:send_packet(FSMPid, PreChunk),
erlcraft_client_fsm:send_packet(FSMPid, Chunk),
+ io:format("Chunks Sent~n", []),
sets:add_element(Key, Acc)
end, ChunkList, ChunksToLoad).
@@ -38,9 +40,12 @@ send_world(FSMPid, ChunkList) ->
init([FSMPid]) ->
io:format("Started!~n", []),
{ok, TRef} = timer:send_interval(1000, update_time),
- mc_world:register_client(self()),
{ok, #state{fsm = FSMPid, timer_ref = TRef, client_start = now(), loc = undefined, chunk_list = sets:new(), player_id = undefined}}.
+handle_call({get_location}, _From, #state{loc = Location} = State) when Location =/= undefined ->
+ {X, Y, Z, _S, _R, _P} = Location,
+ {reply, {loc, X, Y, Z}, State};
+
handle_call(_Request, _From, _State) ->
io:format("Call: ~p~n", [_Request]),
{reply, none, _State}.
@@ -56,11 +61,13 @@ handle_cast({position, X, Y, Z, S, _U}, #state{loc = {_, _, _, _, R, P}, fsm = F
handle_cast({look, R, P, _U}, #state{loc = {X, Y, Z, S, _, _}} = State) ->
{noreply, State#state{loc = {X, Y, Z, S, R, P}}};
-handle_cast({client_begin, PlayerID}, #state{fsm = FSMPid, chunk_list = ChunkList} = State) ->
+handle_cast({client_begin, PlayerID, Username}, #state{fsm = FSMPid, chunk_list = ChunkList} = State) ->
NewChunkList = send_world(FSMPid, ChunkList),
{loc, SpawnX, SpawnY, SpawnZ, SpawnStance, SpawnRotation, SpawnPitch} = mc_world:get_spawn(),
erlcraft_client_fsm:send_packet(FSMPid, mc_reply:position_and_look(SpawnX, SpawnY, SpawnZ, SpawnStance, SpawnRotation, SpawnPitch)),
erlcraft_client_fsm:send_packet(FSMPid, mc_reply:compass(SpawnX, SpawnY, SpawnZ)),
+ Details = {client_details, random:uniform(1024), binary_to_list(Username), SpawnX, SpawnY, SpawnZ, SpawnRotation, SpawnPitch, 0},
+ mc_world:register_client(self(), Details),
{noreply, State#state{player_id = PlayerID, chunk_list = NewChunkList, loc = {SpawnX, SpawnY, SpawnZ, SpawnStance, SpawnRotation, SpawnPitch}}};
handle_cast({flying, _Flying}, State) ->
@@ -70,6 +77,12 @@ handle_cast({packet, Data}, #state{fsm = FSMPid} = State) ->
erlcraft_client_fsm:send_packet(FSMPid, Data),
{noreply, State};
+handle_cast({give_item, ID, ItemID}, #state{fsm = FSMPid, player_id = PlayerID} = State) ->
+ erlcraft_client_fsm:send_packet(FSMPid, mc_reply:collect_item(ID, PlayerID)),
+ erlcraft_client_fsm:send_packet(FSMPid, mc_reply:add_to_inventory(ItemID, 1, 0)),
+ {noreply, State};
+
+
handle_cast({kick, Message}, State) ->
io:format("Kick: ~p~n", [Message]),
{stop, normal, State};
View
17 src/mc.erl
@@ -74,8 +74,8 @@ handle_data(Data) ->
{done, {update, Time}, Rest};
% 0x05 - Inventory
?PKT_PLAYER_INVENTORY(Type, Count) ->
- {Data, Rest} = parse_inventory(Count, More),
- {done, {inventory, Type, Data}, Rest};
+ {Inventory, Rest} = parse_inventory(Count, More),
+ {done, {inventory, Type, Inventory}, Rest};
% 0x06 - Spawn(Compass location)
?PKT_COMPASS(X, Y, Z) ->
{done, {compass, X, Y, Z}, Rest};
@@ -166,12 +166,15 @@ handle_data(Data) ->
parse_inventory(Count, Data) ->
parse_inventory(Count, Data, []).
+parse_inventory(0, Data, Items) ->
+ {Items, Data};
+
parse_inventory(Count, Data, Items) ->
case Data of
- <<-1/integer-signed-big, MoreData/binary>> ->
+ <<Val:16/integer-signed-big, MoreData/binary>> when Val =:= -1 ->
parse_inventory(Count-1, MoreData, [{empty}|Items]);
- <<?PKT_SHORT(ItemID), ?PKT_BYTE(Count), ?PKT_SHORT(Health), MoreData/binary>> ->
- parse_inventory(Count-1, MoreData, [{item, ItemID, Count, Health}|Items])
+ <<?PKT_SHORT(ItemID), ?PKT_BYTE(ItemCount), ?PKT_SHORT(Health), MoreData/binary>> ->
+ parse_inventory(Count-1, MoreData, [{item, ItemID, ItemCount, Health}|Items])
end.
@@ -179,9 +182,9 @@ handle_packet(_Client, {handshake, _PlayerName}) ->
% io:format("C->S: Welcome: ~p~n", [PlayerName]),
mc_reply:handshake(false);
-handle_packet(Client, {login, PlayerID, _Username, _Password}) ->
+handle_packet(Client, {login, PlayerID, Username, _Password}) ->
% io:format("C->S: PlayerID: ~p~nLogin: ~p~nPass: ~p~n", [PlayerID, Username, Password]),
- gen_server:cast(Client, {client_begin, PlayerID}),
+ gen_server:cast(Client, {client_begin, PlayerID, Username}),
mc_reply:login(PlayerID, "", "");
handle_packet(Client, {player_move_look, X, Y, Z, S, R, P, _U}) ->
View
11 src/mc_reply.erl
@@ -37,6 +37,17 @@ block_change(X, Y, Z, Type, Meta) ->
compass(X, Y, Z) ->
mc_util:write_packet(16#06, [{int, trunc(X)}, {int, trunc(Y)}, {int, trunc(Z)}]).
+named_spawn(ID, Name, X, Y, Z, Rotation, Pitch, Item) ->
+ mc_util:write_packet(16#14, [{int, ID}, {string, Name}, {int, trunc(X)}, {int, trunc(Y)}, {int, trunc(Z)}, {byte, trunc(Rotation)}, {byte, trunc(Pitch)}, {short, Item}]).
+
+item_spawn(ID, Item, U1, X, Y, Z, R, P, U2) ->
+ mc_util:write_packet(16#15, [{int, ID}, {short, Item}, {byte, U1}, {int, X}, {int, Y}, {int, Z}, {byte, R}, {byte, P}, {byte, U2}]).
+
+collect_item(CollectedID, CollectorID) ->
+ mc_util:write_packet(16#16, [{int, CollectedID}, {int, CollectorID}]).
+add_to_inventory(ItemID, Amount, Health) ->
+ mc_util:write_packet(16#11, [{short, ItemID}, {byte, Amount}, {short, Health}]).
+
fake_world(Pid, BlockCount, LocX, _LocY, LocZ) ->
Width = math:sqrt(BlockCount),
View
75 src/mc_world.erl
@@ -4,10 +4,11 @@
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
--record(state, {world_path, world, chunk_store, clients, world_updates}).
+-record(state, {world_path, world, chunk_store, block_store, clients, world_updates}).
+-define(SERVER_TIMEOUT, 30000).
%% External API
--export([start_link/1, get_chunk/3, get_spawn/0, load_chunk/5, dbg_chunk/3, block_dig/6, register_client/1]).
+-export([start_link/1, get_chunk/3, get_spawn/0, load_chunk/5, dbg_chunk/3, block_dig/6, register_client/2]).
generate_pre_chunk(X,Z, Update) ->
mc_util:write_packet(16#32, [{int, X}, {int, Z}, {bool, Update}]).
@@ -45,7 +46,10 @@ block_dig(State, X, Y, Z, Direction, _Client, ChunkStore) when State =:= 3 ->
<<Before:Offset/binary, Block:8/integer, After/binary>> = BD,
NBD = <<Before/binary, 0:8/integer, After/binary>>,
ets:insert(ChunkStore, {Key, NBD, MD, WL}),
- {block, X, Y, Z, 0, 8};
+ % Random location inside the block that was broken
+ RX = X * 32 + 8 + random:uniform() * 16,
+ RZ = Z * 32 + 8 + random:uniform() * 16,
+ [{block, X, Y, Z, 0, 8}, {spawn, random:uniform(8192), Block, 1, RX, (Y+1) * 32, RZ, 0, 0, 0}];
Else ->
io:format("unknown: ~p key: ~p~n", [Else, Key]),
ok
@@ -130,16 +134,16 @@ dbg_chunk(Blocks, MetaData, LightData) ->
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}).
+ gen_server:call(?MODULE, {block_dig, Stage, X, Y, Z, Direction, Client}, ?SERVER_TIMEOUT).
get_spawn() ->
- gen_server:call(?MODULE, {get_spawn}).
+ gen_server:call(?MODULE, {get_spawn}, ?SERVER_TIMEOUT).
get_chunk(X,Y,Z) ->
- gen_server:call(?MODULE, {get_chunk, trunc(X), trunc(Y), trunc(Z)}).
+ gen_server:call(?MODULE, {get_chunk, trunc(X), trunc(Y), trunc(Z)}, ?SERVER_TIMEOUT).
-register_client(Client) ->
- gen_server:call(?MODULE, {register_client, Client}).
+register_client(Client, Details) ->
+ gen_server:call(?MODULE, {register_client, Client, Details}, ?SERVER_TIMEOUT).
start_link(World) ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [World], []).
@@ -154,10 +158,17 @@ init([MapName]) ->
LevelPath = string:join([Path, "level.dat"], "/"),
World = nbt:load_file(LevelPath),
ChunkStore = ets:new(world, []),
- {ok, _TRef} = timer:send_interval(500, flush_world_updates),
- {ok, #state{world_path = Path, world = World, chunk_store = ChunkStore, clients = [], world_updates = []}}.
-
-handle_call({register_client, Client}, _From, #state{clients = Clients} = State) when is_pid(Client) ->
+ BlockStore = ets:new(blocks, []),
+ timer:send_after(500, flush_world_updates),
+ {ok, #state{world_path = Path, world = World, chunk_store = ChunkStore, clients = [], world_updates = [], block_store = BlockStore}}.
+
+handle_call({register_client, Client, Details}, _From, #state{clients = Clients} = State) when is_pid(Client) ->
+ lists:foreach(fun(C) ->
+ {client, ClientPid} = C,
+ {client_details, ID, Name, X, Y, Z, R, P, I} = Details,
+ io:format("Sending named spawn[~p]: ~p to ~p~n", [ID, Name, ClientPid]),
+ gen_server:cast(ClientPid, {packet, mc_reply:named_spawn(ID, Name, X ,Y, Z, R, P, I)})
+ end, Clients),
NewClients = lists:keystore(Client, 2, Clients, {client, Client}),
io:format("Registering client: ~p~n", [Client]),
{reply, ok, State#state{clients = NewClients}};
@@ -195,8 +206,9 @@ handle_call({get_spawn}, _From, #state{world = World} = State) ->
handle_call({block_dig, Stage, X, Y, Z, Direction, Client}, _From, #state{chunk_store = ChunkStore, world_updates = WorldUpdates} = State) ->
NewUpdates = case block_dig(Stage, X, Y, Z, Direction, Client, ChunkStore) of
- ok -> WorldUpdates;
- Update -> [Update | WorldUpdates]
+ ok ->
+ WorldUpdates;
+ Update -> lists:flatten([Update | WorldUpdates])
end,
{reply, none, State#state{world_updates = NewUpdates}};
@@ -208,15 +220,42 @@ handle_cast(_Request, _State) ->
io:format("Cast: ~p~n", [_Request]),
{noreply, _State}.
-handle_info(flush_world_updates, #state{clients = Clients, world_updates = WorldUpdates} = State) ->
- lists:foreach(fun(Block) ->
- {block, X, Y, Z, Type, Meta} = Block,
+handle_info(flush_world_updates, #state{clients = Clients, world_updates = WorldUpdates, block_store = BlockStore} = State) ->
+ lists:foreach(fun(Item) ->
+ Packet = case Item of
+ {block, X, Y, Z, Type, Meta} ->
+ mc_reply:block_change(trunc(X), trunc(Y), trunc(Z), Type, Meta);
+ {spawn, ID, ItemID, U1, X, Y, Z, R, P, U2} ->
+ Key = {trunc(X/32), trunc(Z/32)},
+ io:format("Spawn block at: ~p~n", [Key]),
+ case ets:lookup(BlockStore, Key) of
+ [] ->
+ ets:insert(BlockStore, {Key, [{ID, ItemID}]});
+ {Key, Values} ->
+ ets:insert(BlockStore, {Key, [{ID, ItemID}|Values]})
+ end,
+ mc_reply:item_spawn(ID, ItemID, U1, trunc(X), trunc(Y), trunc(Z), R, P, U2)
+ end,
lists:foreach(fun(Client) ->
- Packet = mc_reply:block_change(trunc(X), trunc(Y), trunc(Z), Type, Meta),
{client, ClientPid} = Client,
gen_server:cast(ClientPid, {packet, Packet})
end, Clients)
end, WorldUpdates),
+ BlocksSent = lists:foldl(fun(Client, Acc) ->
+ {client, ClientPid} = Client,
+ {loc, X, _Y, Z} = gen_server:call(ClientPid, {get_location}, infinity),
+ Key = {trunc(X), trunc(Z)},
+ io:format("Key: ~p~n", [Key]),
+ case ets:lookup(BlockStore, Key) of
+ [{Key, Values}] ->
+ io:format("Found block for client~n", []),
+ [gen_server:cast(ClientPid, {give_item, ID, ItemID}) || {ID, ItemID} <- Values],
+ [Key | Acc];
+ _ -> Acc
+ end
+ end, [], Clients),
+ [ets:delete(BlockStore, Key) || Key <- BlocksSent],
+ timer:send_after(500, flush_world_updates),
{noreply, State#state{world_updates = []}};
handle_info(_Info, _State) ->
Please sign in to comment.
Something went wrong with that request. Please try again.