• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Tags
No Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

First Machine Age's Mods (Combined repo.)


Commit MetaInfo

Revisão3195994595d48d697d116e78f1e42b17169a3214 (tree)
Hora2019-11-18 08:28:10
Autormelchior <melchior@user...>
Commitermelchior

Mensagem de Log

W.I.P. #8; Nearly there

Mudança Sumário

Diff

--- a/AccessControls/AccessControlMod.cs
+++ b/AccessControls/AccessControlMod.cs
@@ -381,7 +381,7 @@ namespace FirstMachineAge
381381 IServerPlayer serverPlayer = player as IServerPlayer;
382382 Vec3i chunkPos = ServerAPI.World.BlockAccessor.ToChunkPos(blockPos);
383383
384- Mod.Logger.VerboseDebug("Applying lock; {0} T{1} @ {2} by {3}", theLock.LockStyle, theLock.LockTier, blockSel.Position, player.PlayerName);
384+ Mod.Logger.VerboseDebug("Applying lock; {0} T{1} @ {2} by {3}", theLock.LockStyle, theLock.LockTier, blockPos, player.PlayerName);
385385
386386 AccessControlNode newLockACN = new AccessControlNode(player.PlayerUID, theLock.LockStyle );
387387
@@ -399,7 +399,7 @@ namespace FirstMachineAge
399399 }
400400
401401 if (theLock.LockStyle == LockKinds.Key) {
402- newLockACN.KeyID = theLock.KeyID(itemSlot).GetValueOrDefault(PersistedState.KeyId_Sequence);
402+ newLockACN.KeyID = theLock.KeyID(itemSlot).GetValueOrDefault(-1);
403403
404404 //Perform inventory item swap to true Key from lock (create key first...)
405405 GenericKey matchingKey = ServerAPI.World.GetItem(new AssetLocation(_domain,_keyCodeName+"-"+material)) as GenericKey;
@@ -408,7 +408,8 @@ namespace FirstMachineAge
408408 GenericKey.WriteACL_ItemStack(ref itemStackForKey, newLockACN, blockPos);
409409
410410 serverPlayer.InventoryManager.TryGiveItemstack(itemStackForKey, true);
411- //Mark slot dirty?
411+ //Mark slot dirty?
412+ Mod.Logger.VerboseDebug("Created matching Key #{0} for lock@{1}", newLockACN.KeyID, blockPos);
412413 }
413414
414415 if (commitACN)
@@ -540,7 +541,10 @@ namespace FirstMachineAge
540541 return null;
541542 }
542543
543-
544+ public ReadOnlyDictionary<int, KeyValuePair<BlockPos, AccessControlNode>> RetrieveKnownKeys( )
545+ {
546+ return new ReadOnlyDictionary<int, KeyValuePair<BlockPos, AccessControlNode>>(ACNs_byKeyID);
547+ }
544548
545549 protected bool AttemptAccess(IPlayer byPlayer, BlockPos atPosition, byte[] guess = null)
546550 {
--- a/AccessControls/AccessControls.csproj
+++ b/AccessControls/AccessControls.csproj
@@ -113,6 +113,9 @@
113113 <None Include="assets\fma\shapes\item\access_controls\barrel_lock.json">
114114 <CopyToOutputDirectory>Always</CopyToOutputDirectory>
115115 </None>
116+ <None Include="assets\fma\itemtypes\locks\keylock.json">
117+ <CopyToOutputDirectory>Always</CopyToOutputDirectory>
118+ </None>
116119 </ItemGroup>
117120 <ItemGroup>
118121 <ProjectReference Include="..\FirstMachineAge_Common\Common.csproj">
--- a/AccessControls/AccessControls_Internals.cs
+++ b/AccessControls/AccessControls_Internals.cs
@@ -24,7 +24,7 @@ namespace FirstMachineAge
2424 /// </summary>
2525 public partial class AccessControlsMod
2626 {
27- private const string _domain = "FMA";
27+ private const string _domain = @"fma";
2828 private const string _AccessControlNodesKey = @"ACCESS_CONTROL_NODES";
2929 private const string _channel_name = @"AccessControl";
3030 internal const string _KeyIDKey = @"key_id";//for JSON attribute, DB key sequence
@@ -40,12 +40,17 @@ namespace FirstMachineAge
4040 private ICoreClientAPI ClientAPI;
4141
4242 //private ModSystemBlockReinforcement brs;
43-
43+ private ACLPersisted PersistedState;//Holds; Sequence counters...
4444 private Dictionary<Vec3i, ChunkACNodes> Server_ACN;//Track changes - and commit every ## minutes, in addition to server shutdown data-storage, chunk unloads
4545 private Dictionary<BlockPos, LockCacheNode> Client_LockLookup;//By BlockPos - for fast local lookup. pre-computed by server...
46- private ACLPersisted PersistedState;//Holds; Sequence counters...
46+
4747 private SortedDictionary<string, HashSet<Vec3i>> previousChunkSet_byPlayerUID;
48- private SortedDictionary<string, HashSet<int>> playerKeyIDs_byPlayerUID;//Future thread access collision?
48+ private SortedDictionary<int, KeyValuePair<BlockPos, AccessControlNode>> ACNs_byKeyID;//Built on extraction of ACN when loading chunks [1:1]
49+
50+ private SortedDictionary<string, HashSet<int>> playerKeyIDs_byPlayerUID;//All Keys ~current
51+ private SortedDictionary<string, HashSet<int>> playerLostKeyIDs_byPlayerUID;//Kept only for single cycle
52+ private SortedDictionary<string, HashSet<int>> playerGainKeyIDs_byPlayerUID;//Kept only for single cycle
53+
4954
5055 //Comm. Channels
5156 private IClientNetworkChannel accessControl_ClientChannel;
@@ -78,6 +83,9 @@ namespace FirstMachineAge
7883 Server_ACN = new Dictionary<Vec3i, ChunkACNodes>();
7984 previousChunkSet_byPlayerUID = new SortedDictionary<string, HashSet<Vec3i>>( );
8085 playerKeyIDs_byPlayerUID = new SortedDictionary<string, HashSet<int>>( );
86+ playerLostKeyIDs_byPlayerUID = new SortedDictionary<string, HashSet<int>>( );
87+ playerGainKeyIDs_byPlayerUID = new SortedDictionary<string, HashSet<int>>( );
88+ ACNs_byKeyID = new SortedDictionary<int, KeyValuePair<BlockPos, AccessControlNode>>( );
8189
8290 //Await lock-GUI events, send cache updates via NW channel...
8391 accessControl_ServerChannel = ServerAPI.Network.RegisterChannel(_channel_name);
@@ -101,6 +109,7 @@ namespace FirstMachineAge
101109 ServerAPI.Event.ServerRunPhase(EnumServerRunPhase.Shutdown, PersistACLData);
102110 //TODO: Also chunk unload events??? (ideally - should be moot since data would mabey be already saved?)
103111 ServerAPI.Event.DidBreakBlock += RemoveACN_byBlockBreakage;
112+ ServerAPI.RegisterCommand(new LocksmithCmd(this.ServerAPI));
104113
105114 Mod.Logger.StoryEvent("...a tumbler turns, and opens\t*click*");
106115 Mod.Logger.VerboseDebug("ACN done server-side Init");
@@ -112,12 +121,12 @@ namespace FirstMachineAge
112121 api.RegisterItemClass("ItemCombolock", typeof(ItemCombolock));
113122 api.RegisterItemClass("ItemKeylock", typeof(ItemKeylock));
114123 api.RegisterItemClass("ItemKey", typeof(GenericKey));
115- api.RegisterBlockBehaviorClass("Lockable", typeof(BlockBehaviorComplexLockable));
124+ api.RegisterBlockBehaviorClass("Lockable", typeof(BlockBehaviorComplexLockable));
116125 }
117126
118127 private void InitializeClientSide( )
119128 {
120- if (ClientAPI.Network.DidReceiveChannelId(_channel_name)) Mod.Logger.VerboseDebug("Server side channel: \"{0}\" existant", _channel_name);
129+ Mod.Logger.VerboseDebug("Server side channel: \"{0}\" is {1} ", _channel_name,ClientAPI.Network.GetChannelState(_channel_name));
121130 accessControl_ClientChannel = ClientAPI.Network.RegisterChannel(_channel_name);
122131 accessControl_ClientChannel = accessControl_ClientChannel.RegisterMessageType<LockGUIMessage>( );
123132 accessControl_ClientChannel = accessControl_ClientChannel.RegisterMessageType<LockStatusList>( );
@@ -145,9 +154,9 @@ namespace FirstMachineAge
145154 return true;
146155 }
147156
148- internal void LoadACN_fromChunk(Vec3i chunkPos)
157+ internal void LoadACN_fromChunk(Vec3i chunkPos, bool verboseMsg = false)
149158 {
150- //ATTEMPT to Retrieve and add to local cache...
159+ //ATTEMPT to Retrieve and add to local cache: ACN, KeyID lookups...
151160 IServerChunk targetChunk;
152161 byte[ ] data = null;
153162 long chunkIndex = ServerAPI.World.BulkBlockAccessor.ToChunkIndex3D(chunkPos);
@@ -173,9 +182,24 @@ namespace FirstMachineAge
173182 #endif
174183
175184 Server_ACN.Add(chunkPos.Clone( ), existingNodes);
185+
186+ var keyCounter = existingNodes.Entries.Count(acn => acn.Value.LockStyle == LockKinds.Key);
187+ if (keyCounter > 0)
188+ {
189+ Mod.Logger.VerboseDebug("ACN has {0} key-lock entries to track", keyCounter);
190+
191+ foreach (KeyValuePair<BlockPos, AccessControlNode> kvp in existingNodes.Entries.Where(acn => acn.Value.LockStyle == LockKinds.Key))
192+ {
193+ if (kvp.Value.KeyID.HasValue) {
194+ ACNs_byKeyID.Add(kvp.Value.KeyID.Value, kvp);
195+ }
196+ }
197+
198+ }
199+
176200 } else {
177201 #if DEBUG
178- Mod.Logger.VerboseDebug("Absent ACN data for chunk: {0}! (placeholder added)", chunkPos);
202+ if (verboseMsg) Mod.Logger.VerboseDebug("Absent ACN data for chunk: {0}! (placeholder added)", chunkPos);
179203 #endif
180204 //Setup new AC Node list for this chunk.
181205 ChunkACNodes placeHolderNodes = new ChunkACNodes();
@@ -188,11 +212,35 @@ namespace FirstMachineAge
188212 internal void AddACN_ToServerACNs(BlockPos blockPos, AccessControlNode node )
189213 {
190214 Vec3i chunkPos = ServerAPI.World.BlockAccessor.ToChunkPos(blockPos);
215+ bool success = false;
191216
192217 if (Server_ACN.ContainsKey(chunkPos) ) {
193- Mod.Logger.Debug("Appending to ChunkACNodes at {0}", chunkPos);
194- Server_ACN[chunkPos].Entries.Add(blockPos, node);
195- Server_ACN[chunkPos].Altered = true;
218+
219+ if (Server_ACN[chunkPos].Entries.ContainsKey(blockPos))
220+ {
221+ var existingACN = Server_ACN[chunkPos].Entries[blockPos];
222+ if (existingACN.LockStyle == LockKinds.None) {
223+ Server_ACN[chunkPos].Entries[blockPos] = node;
224+ Server_ACN[chunkPos].Altered = true;
225+ Mod.Logger.Debug("Overwrote 'None' -> '{0}', Chunk at {1}", node.LockStyle, chunkPos);
226+ success = true;
227+ }
228+ else {
229+ Mod.Logger.Error("Rejecting overwrite of ACN @{0} was: ({1}){2} to ({3}){4}", blockPos,
230+ existingACN.OwnerPlayerUID,
231+ existingACN.LockStyle,
232+ node.OwnerPlayerUID,
233+ node.LockStyle
234+ );
235+ }
236+ }
237+ else {
238+ Mod.Logger.Debug("Appending New ACN Chunk at {0}", chunkPos);
239+ Server_ACN[chunkPos].Entries.Add(blockPos, node);
240+ Server_ACN[chunkPos].Altered = true;
241+ success = true;
242+ }
243+
196244 }
197245 else {
198246 Mod.Logger.Debug("Created ChunkACNodes for {0}", chunkPos);
@@ -200,8 +248,19 @@ namespace FirstMachineAge
200248 Server_ACN[chunkPos].Entries.Add(blockPos, node);
201249 Server_ACN[chunkPos].Altered = true;
202250 Server_ACN[chunkPos].OriginChunk = chunkPos.Clone( );
251+ success = true;
203252 }
204253
254+ if (success && node.LockStyle == LockKinds.Key) {
255+ if (ACNs_byKeyID.ContainsKey(node.KeyID.Value)) {
256+ //Duplicate??
257+ Mod.Logger.Error("Duplicate ACN_byKeyID ?! #{0} @{1}", node.KeyID.Value, blockPos);
258+ }
259+ else {
260+ ACNs_byKeyID.Add(node.KeyID.Value, new KeyValuePair<BlockPos, AccessControlNode>(blockPos, node));
261+ }
262+
263+ }
205264 }
206265
207266 private void PreloadACLData( )
@@ -357,6 +416,9 @@ namespace FirstMachineAge
357416
358417 foreach (var update in networkMessage.LockStatesByBlockPos)
359418 {
419+ #if DEBUG
420+ if (update.Value.LockState != LockStatus.None) Mod.Logger.VerboseDebug("pos {0} LS: {1}", update.Key, update.Value.LockState);
421+ #endif
360422 if (Client_LockLookup.ContainsKey(update.Key))
361423 {
362424 //Replace
@@ -453,7 +515,8 @@ namespace FirstMachineAge
453515 wake:
454516 Mod.Logger.VerboseDebug("Portunus thread awoken");
455517 try {
456- //####################### Fetch ACN's for *INTRODUCED* (to A.C. system) Chunks... ######################
518+ //####################### Fetch ACN's for *INTRODUCED* (to A.C. system) Chunks... ######################
519+ uint newChunkCount = 0;
457520 foreach (var chunkEntry in ServerAPI.WorldManager.AllLoadedChunks) {
458521
459522 Vec3i loadedPos = ServerMAIN.WorldMap.ChunkPosFromChunkIndex3D(chunkEntry.Key);
@@ -461,12 +524,13 @@ namespace FirstMachineAge
461524 if (Server_ACN.ContainsKey(loadedPos) == false)
462525 {
463526 LoadACN_fromChunk(loadedPos);
527+ newChunkCount++;
464528 }
465529 }
530+ if (newChunkCount > 0) Mod.Logger.Debug("Noticed {0} new chunks", newChunkCount);
466531
467532 //####################### For all online players - ACN's for *new* chunks entered/in ###########################
468533 foreach (var player in ServerAPI.World.AllOnlinePlayers) {//TODO: Parallel.ForEach
469-
470534 //var client = this.ServerMAIN.GetConnectedClient(player.PlayerUID);
471535
472536 var center = ServerAPI.World.BlockAccessor.ToChunkPos(player.Entity.ServerPos.AsBlockPos.Copy( ));
@@ -474,7 +538,6 @@ namespace FirstMachineAge
474538 HashSet<Vec3i> alreadyUpdatedSet = null;
475539 if (previousChunkSet_byPlayerUID.TryGetValue(player.PlayerUID, out alreadyUpdatedSet))
476540 {
477-
478541 //All of them for nearest 27 CHUNKs, contacting ['new' Chunk ]
479542 var fresh_chunks = Helpers.ComputeChunkBubble(center).Except(alreadyUpdatedSet).ToList( );
480543
@@ -487,21 +550,51 @@ namespace FirstMachineAge
487550 introducedNodes.AddRange(ACNs_byChunk);
488551 }
489552 }
553+ if (introducedNodes.Count > 0)
554+ {
555+ #if DEBUG
556+ Mod.Logger.VerboseDebug("Player {0} will get {1} ACNs about chunk {2}", player.PlayerName, introducedNodes.Count, center);
557+ #endif
490558
491- if (introducedNodes.Count > 0) {
492- #if DEBUG
493- Mod.Logger.VerboseDebug("Player {0} will get {1} ACNs about chunk {2}", player.PlayerName, introducedNodes.Count, center);
494- #endif
495-
496- SendClientACNMultiUpdates(player as IServerPlayer, introducedNodes);
559+ SendClientACNMultiUpdates(player as IServerPlayer, introducedNodes);
497560
498- previousChunkSet_byPlayerUID[player.PlayerUID].AddRange(fresh_chunks);
561+ previousChunkSet_byPlayerUID[player.PlayerUID].AddRange(fresh_chunks);
562+ }
563+ }
499564 }
565+
566+ //################### Key holding status changes ########################
567+ if (playerLostKeyIDs_byPlayerUID.Count > 0 || playerGainKeyIDs_byPlayerUID.Count > 0) {
568+ Stack<string> playerKeyChanges = new Stack<string>( playerGainKeyIDs_byPlayerUID.Keys.Union(playerLostKeyIDs_byPlayerUID.Keys));
569+
570+ while (playerKeyChanges.Count > 0) {
571+ string pid = playerKeyChanges.Pop( );
572+
573+ IServerPlayer tgtPlayer = ServerAPI.World.PlayerByUid(pid) as IServerPlayer;
574+
575+ Mod.Logger.VerboseDebug("Player {0} held key(s) changed ~ re-evaluating ACNs", tgtPlayer.PlayerName);
576+ //Extract ACN from Key's stored position data...
577+
578+ var ACNs_comboKeys = new List<KeyValuePair<BlockPos, AccessControlNode>>( );
579+
580+ if (playerLostKeyIDs_byPlayerUID.ContainsKey(pid)) ACNs_comboKeys = GetACNs_byKeyID(playerLostKeyIDs_byPlayerUID[pid]);
581+
582+ if (playerGainKeyIDs_byPlayerUID.ContainsKey(pid)) ACNs_comboKeys = ACNs_comboKeys.Concat(GetACNs_byKeyID(playerGainKeyIDs_byPlayerUID[pid])).ToList();
583+
584+ if ( ACNs_comboKeys.Count > 0) {
585+ var keyCount = playerKeyIDs_byPlayerUID.ContainsKey(pid) ? playerKeyIDs_byPlayerUID [pid].Count: 0;
586+ Mod.Logger.VerboseDebug("Player {0} - {1} key(s) updates for {2} ACNs", tgtPlayer.PlayerName, keyCount ,ACNs_comboKeys.Count);
587+ SendClientACNMultiUpdates(tgtPlayer, ACNs_comboKeys);
588+
589+ //Done set - cleanup;
590+ if (playerGainKeyIDs_byPlayerUID.ContainsKey(pid)) playerGainKeyIDs_byPlayerUID[pid].Clear( );
591+ if (playerLostKeyIDs_byPlayerUID.ContainsKey(pid)) playerLostKeyIDs_byPlayerUID[pid].Clear( );
500592 }
501- //Keys should already be marking their previous/current owner (called externally) - ACN
502593
503594 }
504595
596+ }
597+
505598 //########################### Persist & SAVE-COMMIT Altered ACNs ! #########################
506599 var alteredCount = Server_ACN.Count(ac => ac.Value.Altered == true);
507600 if (alteredCount > 0) Mod.Logger.Debug("There are {0} altered chunk Nodes to persist", alteredCount);
@@ -536,7 +629,6 @@ namespace FirstMachineAge
536629 }
537630 }
538631
539-
540632 private LockStatusList ComputeLSLFromACNs(ICollection<KeyValuePair<BlockPos, AccessControlNode>> nodesByPos, IServerPlayer byPlayer)
541633 {
542634 var stati = new Dictionary<BlockPos, LockCacheNode>( );
@@ -676,7 +768,7 @@ namespace FirstMachineAge
676768 {
677769 var keyId = GenericKey.KeyID(slot.Itemstack);
678770 //PlayerUID ?
679- if (keyId > 0) keyIds.Add(keyId);
771+ if (keyId > 0) keyIds.Add(keyId.Value);
680772 }
681773 }
682774
@@ -716,32 +808,40 @@ namespace FirstMachineAge
716808 IInventory theInv = player.InventoryManager.GetOwnInventory(inventoryClass);
717809
718810 ItemSlot alteredSlot = theInv[slotNum];
719-
811+
720812 if (alteredSlot.Empty) return;
721-
722- if (alteredSlot.StorageType == EnumItemStorageFlags.General &&
813+
814+ if (
723815 alteredSlot.Itemstack.Class == EnumItemClass.Item &&
724816 alteredSlot.Itemstack.Item.Code.BeginsWith(_domain, _keyCodeName)
725817 )
726818 {
727- #if DEBUG
728- Mod.Logger.VerboseDebug("Key appears on slot# {0} in Inv: {1} for {2}", slotNum, alteredSlot.Inventory.InventoryID, player.PlayerName);
819+ #if DEBUG
820+ Mod.Logger.VerboseDebug("AttainKeyBySlotChange: {0} {1} {2}", player.PlayerName, inventoryClass, slotNum);
821+ Mod.Logger.VerboseDebug("Itemstack(code): {0}", alteredSlot.Itemstack.Collectible.Code);
729822 #endif
730-
731- var ivbp = alteredSlot.Inventory as InventoryBasePlayer;
823+
824+ var ivbp = alteredSlot.Inventory as InventoryBasePlayer;
732825
733826 keyID = GenericKey.KeyID(alteredSlot.Itemstack);
734827 lockLocation = GenericKey.LockLocation(alteredSlot.Itemstack);
735828
829+ #if DEBUG
830+ Mod.Logger.VerboseDebug("Key #{0} gained slot# {1} in Inv: {2} for {3}", keyID, slotNum, alteredSlot.Inventory.InventoryID, player.PlayerName);
831+ #endif
832+
736833 if (keyID.HasValue && playerKeyIDs_byPlayerUID.ContainsKey(playerUID))
737834 {
738835 playerKeyIDs_byPlayerUID[playerUID].Add(keyID.Value);
836+ if (playerGainKeyIDs_byPlayerUID.ContainsKey(playerUID)) { playerGainKeyIDs_byPlayerUID[playerUID].Add(keyID.Value); }
837+ else
838+ {
839+ playerGainKeyIDs_byPlayerUID.Add(playerUID, new HashSet<int>());
840+ playerGainKeyIDs_byPlayerUID[playerUID].Add(keyID.Value);
841+ }
739842 }
740-
741-
742- }
743-
744843
844+ }
745845 }
746846
747847 private void RemoveACN_byBlockBreakage(IServerPlayer byPlayer, int oldblockId, BlockSelection blockSel)
@@ -758,8 +858,7 @@ namespace FirstMachineAge
758858 Server_ACN[chunkPos].Entries.Remove(adjPos);
759859
760860 UpdateBroadcast(byPlayer, adjPos, toRemoveACN);
761-
762- Mod.Logger.Notification("player {0} broke @({1}) with ACN Owned by [{2}]", byPlayer.PlayerName, adjPos, toRemoveACN.OwnerPlayerUID);
861+ if (toRemoveACN.LockStyle != LockKinds.None) Mod.Logger.Notification("player {0} broke @({1}) with ACN Owned by [{2}]", byPlayer.PlayerName, adjPos, toRemoveACN.OwnerPlayerUID);
763862 }
764863 }
765864 }
@@ -773,30 +872,92 @@ namespace FirstMachineAge
773872 IInventory theInv = player.InventoryManager.GetOwnInventory(inventoryClass);
774873
775874 ItemSlot alteredSlot = theInv[slotNum];
776-
875+
777876 if (alteredSlot.Empty) return;
778877
779- if (alteredSlot.StorageType == EnumItemStorageFlags.General &&
878+ if (
780879 alteredSlot.Itemstack.Class == EnumItemClass.Item &&
781880 alteredSlot.Itemstack.Item.Code.BeginsWith(_domain, _keyCodeName)
782- ) {
881+ )
882+ {
783883 #if DEBUG
784- Mod.Logger.VerboseDebug("Key lost on slot# {0} in Inv: {1} for {2}", slotNum, alteredSlot.Inventory.InventoryID, player.PlayerName);
884+ Mod.Logger.VerboseDebug("LoseKeyBySlotChange: {0} {1} {2}", player.PlayerName, inventoryClass, slotNum);
885+ Mod.Logger.VerboseDebug("Itemstack(code): {0}", alteredSlot.Itemstack.Collectible.Code);
785886 #endif
786887
787- var ivbp = alteredSlot.Inventory as InventoryBasePlayer;
888+ var ivbp = alteredSlot.Inventory as InventoryBasePlayer;
788889
789890 keyID = GenericKey.KeyID(alteredSlot.Itemstack);
790891 lockLocation = GenericKey.LockLocation(alteredSlot.Itemstack);
791892
792- if (keyID.HasValue && playerKeyIDs_byPlayerUID.ContainsKey(playerUID))
893+ #if DEBUG
894+ Mod.Logger.VerboseDebug("Key #{0} lost on slot# {1} in Inv: {2} for {3}", keyID, slotNum, alteredSlot.Inventory.InventoryID, player.PlayerName);
895+ #endif
896+
897+ if (keyID.HasValue && playerKeyIDs_byPlayerUID.ContainsKey(playerUID))
793898 {
794- playerKeyIDs_byPlayerUID[playerUID].Remove(keyID.Value);
899+ playerKeyIDs_byPlayerUID[playerUID].Remove(keyID.Value);//Having duplicate keys - is a problem still.
900+ if (playerLostKeyIDs_byPlayerUID.ContainsKey(playerUID)) { playerLostKeyIDs_byPlayerUID[playerUID].Add(keyID.Value); }
901+ else
902+ {
903+ playerLostKeyIDs_byPlayerUID.Add(playerUID, new HashSet<int>( ));
904+ playerLostKeyIDs_byPlayerUID[playerUID].Add(keyID.Value);
905+ }
795906 }
796907 }
797908 }
798909
910+ private List<KeyValuePair<BlockPos, AccessControlNode>> GetACNs_byKeyID(HashSet<int> setKeyIDs)
911+ {
912+ List<KeyValuePair<BlockPos, AccessControlNode>> acnList = new List<KeyValuePair<BlockPos, AccessControlNode>>( );
913+
914+ foreach (int keyID in setKeyIDs)
915+ {
916+ if (ACNs_byKeyID.ContainsKey(keyID)) {
917+ acnList.Add(ACNs_byKeyID[keyID]);
918+ }//else: log error? missing ACN in Keys#
919+ }
920+
921+ return acnList;
922+ }
923+
924+ private List<AccessControlNode> ExtractACNsFromKeysFromPlayerInventory(string playerID)
925+ {
926+ var targetPlayer = ServerAPI.World.PlayerByUid(playerID);
927+ List<AccessControlNode> associatedACNs = new List<AccessControlNode>( );
928+
929+ foreach (var inventory in targetPlayer.InventoryManager.Inventories) {
930+ foreach (ItemSlot slot in inventory.Value) {
931+ if (slot.Empty) continue;
932+
933+ if (slot.Itemstack.Collectible.ItemClass == EnumItemClass.Item &&
934+ slot.Itemstack.Item.Code.BeginsWith(_domain, _keyCodeName))
935+ {
936+ var keyId = GenericKey.KeyID(slot.Itemstack);
937+ var ACN_location = GenericKey.LockLocation(slot.Itemstack);
799938
939+ if (keyId == null || ACN_location == null) {
940+ Mod.Logger.Error("Corrupt '{3}' Item in {0}'s {2} Slot#{1}", targetPlayer.PlayerName,inventory.Value.GetSlotId(slot), inventory.Value.InventoryID,slot.Itemstack.Item.Code);
941+ continue;
942+ }
943+
944+ Vec3i locksChunk = ServerAPI.World.BlockAccessor.ToChunkPos(ACN_location);
945+
946+ if (Server_ACN.ContainsKey(locksChunk) && Server_ACN[locksChunk].Entries.ContainsKey(ACN_location)) {
947+ var controlNode = Server_ACN[locksChunk].Entries[ACN_location];//Instead by KEY#
948+
949+ associatedACNs.Add(controlNode);
950+ }
951+ else {
952+ Mod.Logger.Warning("Key-lock without ACN match K#{0} @{1}", keyId, ACN_location);
953+ }
954+ }
955+ }
956+
957+ }
958+
959+ return associatedACNs;
960+ }
800961
801962 #endregion
802963 }
--- a/AccessControls/GroupLocksCmd.cs
+++ b/AccessControls/GroupLocksCmd.cs
@@ -17,7 +17,7 @@ namespace FirstMachineAge
1717 this.Description = "Change lock permissions and assigend groupIDs.";
1818 //this.handler += LocksmithParser;
1919 this.Syntax = "grant [group/player] [player-name/group-name] / revoke [group/player] [player-name/group-name]";
20- //this.RequiredPrivilege = "locksmith";
20+ //this.RequiredPrivilege = "grouplocks";
2121
2222 }
2323 }
--- a/AccessControls/LocksmithCmd.cs
+++ b/AccessControls/LocksmithCmd.cs
@@ -1,6 +1,7 @@
11 using System;
22
33 using Vintagestory.API.Common;
4+using Vintagestory.API.Config;
45 using Vintagestory.API.MathTools;
56 using Vintagestory.API.Server;
67 using Vintagestory.API.Util;
@@ -8,25 +9,103 @@ using Vintagestory.GameContent;
89
910 namespace FirstMachineAge
1011 {
11- public class LocksmithCmd : ServerChatCommand
12+ internal sealed class LocksmithCmd : ServerChatCommand
1213 {
1314 private ICoreServerAPI ServerAPI;
15+ private ILogger Logger { get; set; }
16+ private AccessControlsMod AccessControlsMod { get; set; }
1417
15- public LocksmithCmd( )
18+ public LocksmithCmd(ICoreServerAPI _coreAPI)
1619 {
17- this.Command = "locksmith";
18- this.Description = "ALTER LOCKS: Remove or Change keys and combos.";
19- this.handler += LocksmithParser;
20- this.Syntax = "remove / change / downgrade / info ";
21- this.RequiredPrivilege = "locksmith";
20+ this.Command = "locksmith";
21+ this.Description = "ALTER LOCKS: Remove or Change keys and combos.";
22+ this.handler += LocksmithParser;
23+ this.Syntax = "remove / change / downgrade / info ";
24+ this.RequiredPrivilege = "locksmith";
2225
26+ if (_coreAPI.Side.IsServer( ))
27+ {
28+ this.ServerAPI = _coreAPI;
29+ this.Logger = this.ServerAPI.World.Logger;
30+ AccessControlsMod = ServerAPI.World.Api.ModLoader.GetModSystem<AccessControlsMod>( );
31+ }
2332
33+ }
34+
35+ private LocksmithCmd( )
36+ {
2437
2538 }
2639
2740 private void LocksmithParser(IServerPlayer player, int groupId, CmdArgs args)
2841 {
29- throw new NotImplementedException( );
42+ if (args.Length > 0) {
43+ string command = args.PopWord( );
44+ switch (command) {
45+ case "nodes":
46+ PrintNodes(player, groupId, args);
47+ break;
48+
49+ case "keys":
50+ PrintKeys(player, groupId, args);
51+ break;
52+
53+ case "remove":
54+
55+ break;
56+
57+ case "destroy":
58+
59+ break;
60+
61+ default:
62+ player.SendMessage(GlobalConstants.CurrentChatGroup, "unecognised command", EnumChatType.CommandError);
63+ break;
64+
3065 }
31-}
66+
67+ //List All ACN in a chunk (current)
68+ //List All ACN in a chunk {param}
69+ //Remove ALL ACN in a chunk (current)
70+ //Remove All ACN in a chunk {param}
71+ //Destroy pointed Lock (and ACN for it)
72+ }
73+ else {
74+ player.SendMessage(GlobalConstants.CurrentChatGroup, "no help yet", EnumChatType.CommandSuccess);
75+
76+ }
77+
78+ }
79+
80+ private void PrintNodes(IServerPlayer player, int groupId, CmdArgs args )
81+ {
82+ if (args.Length > 0) {
83+ var chunkPos = args.PopVec3i(null);
84+ }
85+ else {
86+ BlockPos location = player.Entity.ServerPos.AsBlockPos; ;
87+ Vec3i chunkPos = ServerAPI.World.BlockAccessor.ToChunkPos(location);
88+
89+ var acn_List = AccessControlsMod.RetrieveACNs_ByChunk(chunkPos);
90+
91+ foreach (var acn in acn_List) {
92+ string name = ServerAPI.World.PlayerByUid(acn.Value.OwnerPlayerUID).PlayerName;
93+ player.SendMessage(GlobalConstants.InfoLogChatGroup, $"Node@{acn.Key}:{acn.Value.LockStyle} own:{name} '{acn.Value.NameOfLock}'\n", EnumChatType.CommandSuccess);
94+ }
95+
96+ }
97+ }
98+
99+ private void PrintKeys(IServerPlayer player, int groupId, CmdArgs args )
100+ {
101+ var key_List = AccessControlsMod.RetrieveKnownKeys( );
102+
103+ foreach (var acn in key_List) {
104+ string name = ServerAPI.World.PlayerByUid(acn.Value.Value.OwnerPlayerUID).PlayerName;
105+ player.SendMessage(GlobalConstants.InfoLogChatGroup, $"Key#{acn.Key} @{acn.Value.Key} {acn.Value.Value.LockStyle} own:{name} '{acn.Value.Value.NameOfLock}'\n", EnumChatType.CommandSuccess);
106+ }
107+ }
108+
109+
110+ }
32111 }
--- a/AccessControls/assets/fma/itemtypes/locks/combolock.json
+++ b/AccessControls/assets/fma/itemtypes/locks/combolock.json
@@ -2,6 +2,7 @@
22 code: "combolock",
33 class: "ItemCombolock",
44 maxstacksize: 1,
5+ storageFlags: 1,
56 variantgroups: [
67 { code: "material", states: ["tinbronze", "blackbronze", "bismuthbronze", "iron", "steel" ] },
78 ],
--- a/AccessControls/assets/fma/itemtypes/locks/key.json
+++ b/AccessControls/assets/fma/itemtypes/locks/key.json
@@ -2,19 +2,20 @@
22 code: "key",
33 class: "ItemKey",
44 maxstacksize: 1,
5+ storageFlags: 1,
56 variantgroups: [
67 { code: "material", states: ["tinbronze", "blackbronze", "bismuthbronze", "iron", "steel" ] },
78 ],
89 textures: {
9- "material": { base: "item/tool/material/{material}" }
10+ "metal": { base: "game:block/metal/plate/{material}" }
1011 },
1112 shape: { base: "item/access_controls/key1" },
1213 creativeinventory: { "general": ["*"], "gadgets": ["*"] },
1314 guiTransform: {
14- translation: { x: 4, y: 0, z: 0 },
15- rotation: { x: 20, y: 16, z: -152 },
15+ translation: { x: -6, y: 27, z: 0 },
16+ rotation: { x: 0, y: 21, z: -151 },
1617 origin: { x: 0.5, y: 0.12, z: 0.5 },
17- scale: 6
18+ scale: 2.99
1819 },
1920 groundTransform: {
2021 translation: { x: 0, y: 0, z: 0 },
@@ -24,9 +25,9 @@
2425 },
2526 fpHandTransform: {
2627 translation: { x: 0, y: 0, z: 0 },
27- rotation: { x: -14, y: -78, z: 17 },
28+ rotation: { x: -81, y: -78, z: 17 },
2829 origin: { x: 0.5, y: 0.1, z: 0.5 },
29- scale: 2.41
30+ scale: 1.5
3031 },
3132 tpHandTransform: {
3233 translation: { x: -0.83, y: -0.36, z: -0.7 },
--- /dev/null
+++ b/AccessControls/assets/fma/itemtypes/locks/keylock.json
@@ -0,0 +1,66 @@
1+{
2+ code: "lockkey",
3+ class: "ItemKeylock",
4+ maxstacksize: 1,
5+ storageFlags: 1,
6+ variantgroups: [
7+ { code: "material", states: ["tinbronze", "blackbronze", "bismuthbronze", "iron", "steel" ] },
8+ ],
9+ textures: {
10+ "keymetal": { base: "game:block/metal/plate/{material}" },
11+ "slotmetal": { base: "game:item/tool/material/copper" },
12+ "body": { base: "game:item/tool/material/iron" },
13+ },
14+ shape: { base: "item/access_controls/barrel_lock" },
15+ attributesByType:
16+ {
17+ "*-tinbronze": {
18+ lockTier:1,
19+ lockStyle:"Key",
20+ },
21+ "*-blackbronze": {
22+ lockTier:1,
23+ lockStyle:"Key",
24+ },
25+ "*-bismuthbronze": {
26+ lockTier:1,
27+ lockStyle:"Key",
28+ },
29+ "*-iron": {
30+ lockTier:2,
31+ lockStyle:"Key",
32+ },
33+ "*-steel": {
34+ lockTier:3,
35+ lockStyle:"Key",
36+ },
37+ "*": {
38+ lockStyle:"Key"
39+ }
40+ },
41+ creativeinventory: { "general": ["*"], "gadgets": ["*"] },
42+ guiTransform: {
43+ translation: { x: -8, y: 20, z: 1 },
44+ rotation: { x: -1, y: 16, z: -152 },
45+ origin: { x: 0.5, y: 0.12, z: 0.5 },
46+ scale: 3.3
47+ },
48+ groundTransform: {
49+ translation: { x: 0, y: 0, z: 0 },
50+ rotation: { x: -90, y: 0, z: 0 },
51+ origin: { x: 0.5, y: 0.2, z: 0.46 },
52+ scale: 3.5
53+ },
54+ fpHandTransform: {
55+ translation: { x: 0, y: 0.09, z: 0 },
56+ rotation: { x: 19, y: -39, z: 28 },
57+ origin: { x: 0.5, y: 0.1, z: 0.5 },
58+ scale: 1.25
59+ },
60+ tpHandTransform: {
61+ translation: { x: -0.83, y: -0.36, z: -0.7 },
62+ rotation: { x: 1, y: 57, z: 18 },
63+ origin: { x: 0.5, y: 0.15, z: 0.5 },
64+ scale: 0.65
65+ }
66+}
--- a/AccessControls/assets/fma/shapes/item/access_controls/barrel_lock.json
+++ b/AccessControls/assets/fma/shapes/item/access_controls/barrel_lock.json
@@ -1,247 +1,222 @@
1-{
2- "editor": {
3- "allAngles": false,
4- "singleTexture": false
5- },
6- "textureWidth": 16,
7- "textureHeight": 16,
8- "textures": {
9- "iron2": "item/tool/material/iron",
10- "iron": "iron",
11- "copper": "item/tool/material/copper"
12- },
13- "elements": [
14- {
15- "name": "root",
16- "from": [ 8.0, 11.0, 0.0 ],
17- "to": [ 9.0, 12.0, 1.0 ],
18- "rotationOrigin": [ 8.0, 11.0, 0.0 ],
19- "faces": {
20- "north": { "texture": "#null", "uv": [ 0.0, 0.0, 1.0, 1.0 ], "enabled": false },
21- "east": { "texture": "#null", "uv": [ 0.0, 0.0, 1.0, 1.0 ], "enabled": false },
22- "south": { "texture": "#null", "uv": [ 0.0, 0.0, 1.0, 1.0 ], "enabled": false },
23- "west": { "texture": "#null", "uv": [ 0.0, 0.0, 1.0, 1.0 ], "enabled": false },
24- "up": { "texture": "#null", "uv": [ 0.0, 0.0, 1.0, 1.0 ], "enabled": false },
25- "down": { "texture": "#null", "uv": [ 0.0, 0.0, 1.0, 1.0 ], "enabled": false }
26- },
27- "children": [
28- {
29- "name": "ubolt_root",
30- "from": [ -1.0, -2.0, 0.5 ],
31- "to": [ 1.0, -1.2, 1.3 ],
32- "rotationOrigin": [ 0.0, 0.0, 0.5 ],
33- "faces": {
34- "north": { "texture": "#iron2", "uv": [ 0.0, 0.0, 2.0, 0.5 ] },
35- "east": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
36- "south": { "texture": "#iron2", "uv": [ 0.0, 0.0, 2.0, 0.5 ] },
37- "west": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
38- "up": { "texture": "#iron2", "uv": [ 0.0, 0.0, 2.0, 0.5 ] },
39- "down": { "texture": "#iron2", "uv": [ 0.0, 0.0, 2.0, 0.5 ], "enabled": false }
40- },
41- "children": [
42- {
43- "name": "uboltA",
44- "from": [ -0.2, -0.1, 0.1 ],
45- "to": [ 2.2, 0.5, 0.7 ],
46- "rotationOrigin": [ -0.2, -0.1, 0.1 ],
47- "faces": {
48- "north": { "texture": "#iron2", "uv": [ 0.0, 0.0, 2.0, 0.5 ] },
49- "east": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
50- "south": { "texture": "#iron2", "uv": [ 0.0, 0.0, 2.0, 0.5 ] },
51- "west": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
52- "up": { "texture": "#iron2", "uv": [ 0.0, 0.0, 2.0, 0.5 ] },
53- "down": { "texture": "#iron2", "uv": [ 0.0, 0.0, 2.0, 0.5 ], "enabled": false }
54- }
55- },
56- {
57- "name": "uboltB",
58- "from": [ -0.2, -1.1, 0.1 ],
59- "to": [ 0.5, -0.1, 0.7 ],
60- "rotationOrigin": [ -0.2, -1.1, 0.1 ],
61- "faces": {
62- "north": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
63- "east": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
64- "south": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
65- "west": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
66- "up": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
67- "down": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ], "enabled": false }
68- }
69- },
70- {
71- "name": "uboltC",
72- "from": [ 1.5, -1.1, 0.1 ],
73- "to": [ 2.2, -0.1, 0.7 ],
74- "rotationOrigin": [ 1.5, -1.1, 0.1 ],
75- "faces": {
76- "north": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
77- "east": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
78- "south": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
79- "west": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
80- "up": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
81- "down": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ], "enabled": false }
82- }
83- }
84- ]
85- },
86- {
87- "name": "uboltD",
88- "from": [ -1.0, -3.0, 0.5 ],
89- "to": [ -0.6, -2.0, 1.3 ],
90- "rotationOrigin": [ 0.0, -1.0, 0.5 ],
91- "faces": {
92- "north": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
93- "east": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
94- "south": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
95- "west": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
96- "up": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
97- "down": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ], "enabled": false }
98- }
99- },
100- {
101- "name": "uboltE",
102- "from": [ 0.6, -3.0, 0.5 ],
103- "to": [ 1.0, -2.0, 1.3 ],
104- "rotationOrigin": [ 1.6, -1.0, 0.5 ],
105- "faces": {
106- "north": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
107- "east": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
108- "south": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
109- "west": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
110- "up": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
111- "down": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ], "enabled": false }
112- }
113- },
114- {
115- "name": "Cube1",
116- "from": [ -2.0, -5.0, 0.1 ],
117- "to": [ 1.5, -3.4, 1.7 ],
118- "rotationOrigin": [ -2.0, -5.0, 0.1 ],
119- "faces": {
120- "north": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 1.5 ] },
121- "east": { "texture": "#iron", "uv": [ 0.0, 0.0, 1.5, 1.5 ] },
122- "south": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 1.5 ] },
123- "west": { "texture": "#iron", "uv": [ 0.0, 0.0, 1.5, 1.5 ] },
124- "up": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 1.5 ] },
125- "down": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 1.5 ] }
126- },
127- "children": [
128- {
129- "name": "Cube9",
130- "from": [ 0.0, 1.6, 0.3 ],
131- "to": [ 3.5, 1.9, 1.3 ],
132- "rotationOrigin": [ 0.0, 1.6, 0.3 ],
133- "faces": {
134- "north": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 0.5 ] },
135- "east": { "texture": "#iron", "uv": [ 0.0, 0.0, 1.0, 0.5 ] },
136- "south": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 0.5 ] },
137- "west": { "texture": "#iron", "uv": [ 0.0, 0.0, 1.0, 0.5 ] },
138- "up": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 1.0 ] },
139- "down": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 1.0 ], "enabled": false }
140- }
141- },
142- {
143- "name": "Cube10",
144- "from": [ 0.0, -0.3, 0.3 ],
145- "to": [ 3.5, 0.0, 1.3 ],
146- "rotationOrigin": [ 0.0, -0.3, 0.3 ],
147- "faces": {
148- "north": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 0.5 ] },
149- "east": { "texture": "#iron", "uv": [ 0.0, 0.0, 1.0, 0.5 ] },
150- "south": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 0.5 ] },
151- "west": { "texture": "#iron", "uv": [ 0.0, 0.0, 1.0, 0.5 ] },
152- "up": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 1.0 ], "enabled": false },
153- "down": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 1.0 ] }
154- }
155- },
156- {
157- "name": "Cube11",
158- "from": [ 0.0, 0.2, -0.3 ],
159- "to": [ 3.5, 1.4, 0.0 ],
160- "rotationOrigin": [ 0.0, 0.2, -0.3 ],
161- "faces": {
162- "north": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 1.0 ] },
163- "east": { "texture": "#iron", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
164- "south": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 1.0 ], "enabled": false },
165- "west": { "texture": "#iron", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
166- "up": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 0.5 ] },
167- "down": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 0.5 ] }
168- }
169- },
170- {
171- "name": "Cube12",
172- "from": [ 0.0, 0.2, 1.6 ],
173- "to": [ 3.5, 1.4, 1.9 ],
174- "rotationOrigin": [ 0.0, 0.2, 1.6 ],
175- "faces": {
176- "north": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 1.0 ], "enabled": false },
177- "east": { "texture": "#iron", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
178- "south": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 1.0 ] },
179- "west": { "texture": "#iron", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
180- "up": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 0.5 ] },
181- "down": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 0.5 ] }
182- }
183- },
184- {
185- "name": "Cube14",
186- "from": [ -1.1, 0.5, 0.6 ],
187- "to": [ -0.1, 1.1, 1.0 ],
188- "rotationOrigin": [ -1.1, 0.5, 0.6 ],
189- "faces": {
190- "north": { "texture": "#iron2", "uv": [ 0.0, 0.0, 1.0, 0.5 ] },
191- "east": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
192- "south": { "texture": "#iron2", "uv": [ 0.0, 0.0, 1.0, 0.5 ] },
193- "west": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
194- "up": { "texture": "#iron2", "uv": [ 0.0, 0.0, 1.0, 0.5 ] },
195- "down": { "texture": "#iron2", "uv": [ 0.0, 0.0, 1.0, 0.5 ] }
196- },
197- "children": [
198- {
199- "name": "Cube15",
200- "from": [ -0.2, 0.4, 0.1 ],
201- "to": [ 0.6, 1.1, 0.3 ],
202- "rotationOrigin": [ -0.2, 0.4, 0.1 ],
203- "faces": {
204- "north": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
205- "east": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
206- "south": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
207- "west": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
208- "up": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
209- "down": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }
210- }
211- },
212- {
213- "name": "Cube16",
214- "from": [ -0.2, -0.5, 0.1 ],
215- "to": [ 0.6, 0.2, 0.3 ],
216- "rotationOrigin": [ -0.2, -0.5, 0.1 ],
217- "faces": {
218- "north": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
219- "east": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
220- "south": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
221- "west": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
222- "up": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
223- "down": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }
224- }
225- }
226- ]
227- }
228- ]
229- },
230- {
231- "name": "Cube13",
232- "from": [ -2.1, -4.9, 0.2 ],
233- "to": [ -2.0, -3.5, 1.6 ],
234- "rotationOrigin": [ -2.1, -4.2, 0.9 ],
235- "rotationX": 45.0,
236- "faces": {
237- "north": { "texture": "#copper", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
238- "east": { "texture": "#copper", "uv": [ 0.0, 0.0, 1.0, 1.0 ] },
239- "south": { "texture": "#copper", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
240- "west": { "texture": "#copper", "uv": [ 0.0, 0.0, 1.0, 1.0 ] },
241- "up": { "texture": "#copper", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
242- "down": { "texture": "#copper", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }
243- }
244- }
245- ]
246- }
1+{
2+ "editor": {
3+ "allAngles": false,
4+ "singleTexture": false
5+ },
6+ "textureWidth": 16,
7+ "textureHeight": 16,
8+ "textures": {
9+ "keymetal": "item/tool/material/copper",
10+ "slotmetal": "item/tool/material/copper",
11+ "body": "item/tool/material/iron"
12+ },
13+ "elements": [
14+ {
15+ "name": "root",
16+ "from": [ 8.0, 11.0, 0.0 ],
17+ "to": [ 9.0, 12.0, 1.0 ],
18+ "rotationOrigin": [ 8.0, 11.0, 0.0 ],
19+ "faces": {
20+ "north": { "texture": "#null", "uv": [ 0.0, 0.0, 1.0, 1.0 ], "enabled": false },
21+ "east": { "texture": "#null", "uv": [ 0.0, 0.0, 1.0, 1.0 ], "enabled": false },
22+ "south": { "texture": "#null", "uv": [ 0.0, 0.0, 1.0, 1.0 ], "enabled": false },
23+ "west": { "texture": "#null", "uv": [ 0.0, 0.0, 1.0, 1.0 ], "enabled": false },
24+ "up": { "texture": "#null", "uv": [ 0.0, 0.0, 1.0, 1.0 ], "enabled": false },
25+ "down": { "texture": "#null", "uv": [ 0.0, 0.0, 1.0, 1.0 ], "enabled": false }
26+ },
27+ "children": [
28+ {
29+ "name": "ubolt",
30+ "from": [ -1.0, -2.0, 0.5 ],
31+ "to": [ 1.0, -1.2, 1.3 ],
32+ "rotationOrigin": [ 0.0, 0.0, 0.5 ],
33+ "faces": {
34+ "north": { "texture": "#body", "uv": [ 0.0, 0.0, 2.0, 0.5 ] },
35+ "east": { "texture": "#body", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
36+ "south": { "texture": "#body", "uv": [ 0.0, 0.0, 2.0, 0.5 ] },
37+ "west": { "texture": "#body", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
38+ "up": { "texture": "#body", "uv": [ 0.0, 0.0, 2.0, 0.5 ] },
39+ "down": { "texture": "#body", "uv": [ 0.0, 0.0, 2.0, 0.5 ], "enabled": false }
40+ },
41+ "children": [
42+ {
43+ "name": "uboltA",
44+ "from": [ -0.2, -0.1, 0.1 ],
45+ "to": [ 2.2, 0.5, 0.7 ],
46+ "rotationOrigin": [ -0.2, -0.1, 0.1 ],
47+ "faces": {
48+ "north": { "texture": "#body", "uv": [ 0.0, 0.0, 2.0, 0.5 ] },
49+ "east": { "texture": "#body", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
50+ "south": { "texture": "#body", "uv": [ 0.0, 0.0, 2.0, 0.5 ] },
51+ "west": { "texture": "#body", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
52+ "up": { "texture": "#body", "uv": [ 0.0, 0.0, 2.0, 0.5 ] },
53+ "down": { "texture": "#body", "uv": [ 0.0, 0.0, 2.0, 0.5 ], "enabled": false }
54+ }
55+ },
56+ {
57+ "name": "uboltB",
58+ "from": [ -0.2, -1.1, 0.1 ],
59+ "to": [ 0.5, -0.1, 0.7 ],
60+ "rotationOrigin": [ -0.2, -1.1, 0.1 ],
61+ "faces": {
62+ "north": { "texture": "#body", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
63+ "east": { "texture": "#body", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
64+ "south": { "texture": "#body", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
65+ "west": { "texture": "#body", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
66+ "up": { "texture": "#body", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
67+ "down": { "texture": "#body", "uv": [ 0.0, 0.0, 0.5, 0.5 ], "enabled": false }
68+ }
69+ },
70+ {
71+ "name": "uboltC",
72+ "from": [ 1.5, -1.1, 0.1 ],
73+ "to": [ 2.2, -0.1, 0.7 ],
74+ "rotationOrigin": [ 1.5, -1.1, 0.1 ],
75+ "faces": {
76+ "north": { "texture": "#body", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
77+ "east": { "texture": "#body", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
78+ "south": { "texture": "#body", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
79+ "west": { "texture": "#body", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
80+ "up": { "texture": "#body", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
81+ "down": { "texture": "#body", "uv": [ 0.0, 0.0, 0.5, 0.5 ], "enabled": false }
82+ }
83+ }
84+ ]
85+ },
86+ {
87+ "name": "Body",
88+ "from": [ -2.0, -5.0, 0.1 ],
89+ "to": [ 1.5, -3.4, 1.7 ],
90+ "rotationOrigin": [ -2.0, -5.0, 0.1 ],
91+ "faces": {
92+ "north": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 1.5 ] },
93+ "east": { "texture": "#body", "uv": [ 0.0, 0.0, 1.5, 1.5 ] },
94+ "south": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 1.5 ] },
95+ "west": { "texture": "#body", "uv": [ 0.0, 0.0, 1.5, 1.5 ] },
96+ "up": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 1.5 ] },
97+ "down": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 1.5 ] }
98+ },
99+ "children": [
100+ {
101+ "name": "Cube9",
102+ "from": [ 0.0, 1.6, 0.3 ],
103+ "to": [ 3.5, 1.9, 1.3 ],
104+ "rotationOrigin": [ 0.0, 1.6, 0.3 ],
105+ "faces": {
106+ "north": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 0.5 ] },
107+ "east": { "texture": "#body", "uv": [ 0.0, 0.0, 1.0, 0.5 ] },
108+ "south": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 0.5 ] },
109+ "west": { "texture": "#body", "uv": [ 0.0, 0.0, 1.0, 0.5 ] },
110+ "up": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 1.0 ] },
111+ "down": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 1.0 ], "enabled": false }
112+ }
113+ },
114+ {
115+ "name": "Cube10",
116+ "from": [ 0.0, -0.3, 0.3 ],
117+ "to": [ 3.5, 0.0, 1.3 ],
118+ "rotationOrigin": [ 0.0, -0.3, 0.3 ],
119+ "faces": {
120+ "north": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 0.5 ] },
121+ "east": { "texture": "#body", "uv": [ 0.0, 0.0, 1.0, 0.5 ] },
122+ "south": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 0.5 ] },
123+ "west": { "texture": "#body", "uv": [ 0.0, 0.0, 1.0, 0.5 ] },
124+ "up": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 1.0 ], "enabled": false },
125+ "down": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 1.0 ] }
126+ }
127+ },
128+ {
129+ "name": "Cube11",
130+ "from": [ 0.0, 0.2, -0.3 ],
131+ "to": [ 3.5, 1.4, 0.0 ],
132+ "rotationOrigin": [ 0.0, 0.2, -0.3 ],
133+ "faces": {
134+ "north": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 1.0 ] },
135+ "east": { "texture": "#body", "uv": [ 0.0, 0.0, 0.25, 1.0 ] },
136+ "south": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 1.0 ], "enabled": false },
137+ "west": { "texture": "#body", "uv": [ 0.0, 0.0, 0.25, 1.0 ] },
138+ "up": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 0.5 ] },
139+ "down": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 0.5 ] }
140+ }
141+ },
142+ {
143+ "name": "Cube12",
144+ "from": [ 0.0, 0.2, 1.6 ],
145+ "to": [ 3.5, 1.4, 1.9 ],
146+ "rotationOrigin": [ 0.0, 0.2, 1.6 ],
147+ "faces": {
148+ "north": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 1.0 ], "enabled": false },
149+ "east": { "texture": "#body", "uv": [ 0.0, 0.0, 0.25, 1.0 ] },
150+ "south": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 1.0 ] },
151+ "west": { "texture": "#body", "uv": [ 0.0, 0.0, 0.25, 1.0 ] },
152+ "up": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 0.5 ] },
153+ "down": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 0.5 ] }
154+ }
155+ }
156+ ]
157+ },
158+ {
159+ "name": "SlotFace",
160+ "from": [ -2.1, -4.9, 0.2 ],
161+ "to": [ -2.0, -3.5, 1.6 ],
162+ "rotationOrigin": [ -2.1, -4.2, 0.9 ],
163+ "rotationX": 45.0,
164+ "faces": {
165+ "north": { "texture": "#slotmetal", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
166+ "east": { "texture": "#slotmetal", "uv": [ 0.0, 0.0, 1.0, 1.0 ] },
167+ "south": { "texture": "#slotmetal", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
168+ "west": { "texture": "#slotmetal", "uv": [ 0.0, 0.0, 1.0, 1.0 ] },
169+ "up": { "texture": "#slotmetal", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
170+ "down": { "texture": "#slotmetal", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }
171+ },
172+ "children": [
173+ {
174+ "name": "KeyStem",
175+ "from": [ -1.0, 0.3, 0.65 ],
176+ "to": [ 0.0, 0.9, 1.05 ],
177+ "rotationOrigin": [ -1.1, 0.5, 0.6 ],
178+ "rotationX": -45.0,
179+ "faces": {
180+ "north": { "texture": "#keymetal", "uv": [ 0.0, 0.0, 1.0, 0.5 ] },
181+ "east": { "texture": "#keymetal", "uv": [ 0.0, 0.0, 0.25, 0.5 ] },
182+ "south": { "texture": "#keymetal", "uv": [ 0.0, 0.0, 1.0, 0.5 ] },
183+ "west": { "texture": "#keymetal", "uv": [ 0.0, 0.0, 0.25, 0.5 ] },
184+ "up": { "texture": "#keymetal", "uv": [ 0.0, 0.0, 1.0, 0.5 ] },
185+ "down": { "texture": "#keymetal", "uv": [ 0.0, 0.0, 1.0, 0.5 ] }
186+ },
187+ "children": [
188+ {
189+ "name": "KeyBit1",
190+ "from": [ -0.2, 0.4, 0.1 ],
191+ "to": [ 0.6, 1.1, 0.3 ],
192+ "rotationOrigin": [ -0.2, 0.4, 0.1 ],
193+ "faces": {
194+ "north": { "texture": "#keymetal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
195+ "east": { "texture": "#keymetal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
196+ "south": { "texture": "#keymetal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
197+ "west": { "texture": "#keymetal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
198+ "up": { "texture": "#keymetal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
199+ "down": { "texture": "#keymetal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }
200+ }
201+ },
202+ {
203+ "name": "KeyBit2",
204+ "from": [ -0.2, -0.5, 0.1 ],
205+ "to": [ 0.6, 0.2, 0.3 ],
206+ "rotationOrigin": [ -0.2, -0.5, 0.1 ],
207+ "faces": {
208+ "north": { "texture": "#keymetal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
209+ "east": { "texture": "#keymetal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
210+ "south": { "texture": "#keymetal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
211+ "west": { "texture": "#keymetal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
212+ "up": { "texture": "#keymetal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
213+ "down": { "texture": "#keymetal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }
214+ }
215+ }
216+ ]
217+ }
218+ ]
219+ }
220+ ]
221+ }
247222 ]}
\ No newline at end of file
--- a/AccessControls/assets/fma/shapes/item/access_controls/key1.json
+++ b/AccessControls/assets/fma/shapes/item/access_controls/key1.json
@@ -1,82 +1,88 @@
11 {
22 "editor": {
33 "allAngles": false,
4- "singleTexture": false
4+ "singleTexture": true
55 },
66 "textureWidth": 16,
77 "textureHeight": 16,
88 "textures": {
9+ "metal": "block/metal/plate/tin"
910 },
1011 "elements": [
1112 {
1213 "name": "Stem",
1314 "from": [ 6.0, 7.0, 8.0 ],
1415 "to": [ 11.0, 7.75, 8.75 ],
16+ "uv": [ 0.0, 0.0 ],
1517 "rotationOrigin": [ 6.0, 7.0, 8.0 ],
1618 "faces": {
17- "north": { "texture": "#null", "uv": [ 0.0, 0.0, 5.0, 0.5 ] },
18- "east": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
19- "south": { "texture": "#null", "uv": [ 0.0, 0.0, 5.0, 0.5 ] },
20- "west": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
21- "up": { "texture": "#null", "uv": [ 0.0, 0.0, 5.0, 0.5 ] },
22- "down": { "texture": "#null", "uv": [ 0.0, 0.0, 5.0, 0.5 ] }
19+ "north": { "texture": "#metal", "uv": [ 0.0, 0.0, 5.0, 0.5 ] },
20+ "east": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
21+ "south": { "texture": "#metal", "uv": [ 0.0, 0.0, 5.0, 0.5 ] },
22+ "west": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
23+ "up": { "texture": "#metal", "uv": [ 0.0, 0.0, 5.0, 0.5 ] },
24+ "down": { "texture": "#metal", "uv": [ 0.0, 0.0, 5.0, 0.5 ] }
2325 },
2426 "children": [
2527 {
2628 "name": "Hole",
2729 "from": [ -1.5, 0.75, 0.0 ],
2830 "to": [ 0.5, 2.75, 1.0 ],
31+ "uv": [ 0.0, 0.0 ],
2932 "rotationOrigin": [ -2.0, 1.0, 0.0 ],
3033 "rotationZ": -45.0,
3134 "faces": {
32- "north": { "texture": "#null", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
33- "east": { "texture": "#null", "uv": [ 0.0, 0.0, 1.0, 2.0 ] },
34- "south": { "texture": "#null", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
35- "west": { "texture": "#null", "uv": [ 0.0, 0.0, 1.0, 2.0 ] },
36- "up": { "texture": "#null", "uv": [ 0.0, 0.0, 2.0, 1.0 ] },
37- "down": { "texture": "#null", "uv": [ 0.0, 0.0, 2.0, 1.0 ] }
35+ "north": { "texture": "#metal", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
36+ "east": { "texture": "#metal", "uv": [ 0.0, 0.0, 1.0, 2.0 ] },
37+ "south": { "texture": "#metal", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
38+ "west": { "texture": "#metal", "uv": [ 0.0, 0.0, 1.0, 2.0 ] },
39+ "up": { "texture": "#metal", "uv": [ 0.0, 0.0, 2.0, 1.0 ] },
40+ "down": { "texture": "#metal", "uv": [ 0.0, 0.0, 2.0, 1.0 ] }
3841 }
3942 },
4043 {
4144 "name": "Tooth1",
4245 "from": [ 5.0, 0.0, 0.0 ],
4346 "to": [ 5.5, 2.0, 0.75 ],
47+ "uv": [ 0.0, 0.0 ],
4448 "rotationOrigin": [ 5.0, 0.0, 0.0 ],
4549 "faces": {
46- "north": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 2.0 ] },
47- "east": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 2.0 ] },
48- "south": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 2.0 ] },
49- "west": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 2.0 ] },
50- "up": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
51- "down": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }
50+ "north": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 2.0 ] },
51+ "east": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 2.0 ] },
52+ "south": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 2.0 ] },
53+ "west": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 2.0 ] },
54+ "up": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
55+ "down": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }
5256 }
5357 },
5458 {
5559 "name": "Tooth2",
5660 "from": [ 5.5, 0.0, 0.0 ],
5761 "to": [ 6.25, 1.0, 0.75 ],
62+ "uv": [ 0.0, 0.0 ],
5863 "rotationOrigin": [ 5.0, 0.0, 0.0 ],
5964 "faces": {
60- "north": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
61- "east": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
62- "south": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
63- "west": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
64- "up": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
65- "down": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }
65+ "north": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
66+ "east": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
67+ "south": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
68+ "west": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 1.0 ] },
69+ "up": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
70+ "down": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }
6671 }
6772 },
6873 {
6974 "name": "Tooth3",
7075 "from": [ 6.25, 0.0, 0.0 ],
7176 "to": [ 6.75, 2.0, 0.75 ],
77+ "uv": [ 0.0, 0.0 ],
7278 "rotationOrigin": [ 6.0, 0.0, 0.0 ],
7379 "faces": {
74- "north": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 2.0 ] },
75- "east": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 2.0 ] },
76- "south": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 2.0 ] },
77- "west": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 2.0 ] },
78- "up": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
79- "down": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }
80+ "north": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 2.0 ] },
81+ "east": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 2.0 ] },
82+ "south": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 2.0 ] },
83+ "west": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 2.0 ] },
84+ "up": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] },
85+ "down": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }
8086 }
8187 }
8288 ]
--- a/AccessControls/data/ACLPersisted.cs
+++ b/AccessControls/data/ACLPersisted.cs
@@ -11,6 +11,11 @@ namespace FirstMachineAge
1111 [ProtoContract]
1212 public class ACLPersisted
1313 {
14+ public ACLPersisted( )
15+ {
16+ KeyId_Sequence = 1;
17+ }
18+
1419 [ProtoMember(1)]
1520 public int KeyId_Sequence;
1621
--- a/AccessControls/items/GenericKey.cs
+++ b/AccessControls/items/GenericKey.cs
@@ -1,24 +1,25 @@
11 using System;
22 using System.Text;
3-
3+using Vintagestory.API.Client;
44 using Vintagestory.API.Common;
55 using Vintagestory.API.MathTools;
6+using Vintagestory.API.Server;
67
78 namespace FirstMachineAge
89 {
910 public class GenericKey : Item
10- {
11+ {
1112
12- public static int KeyID(ItemStack sourceStack)
13+ public static int? KeyID(ItemStack sourceStack)
1314 {
1415 if (sourceStack.Attributes.HasAttribute(AccessControlsMod._KeyIDKey)) {
1516 return sourceStack.Attributes.GetInt(AccessControlsMod._KeyIDKey);
1617 } else {
17- return new int( );
18+ return null;
1819 }
1920 }
2021
21- public static BlockPos LockLocation(ItemStack sourceStack)
22+ public static BlockPos LockLocation(ItemStack sourceStack)//Location of matching ACN
2223 {
2324 if (sourceStack.Attributes.HasAttribute(AccessControlsMod._LockLocationKey)) {
2425 return sourceStack.Attributes.GetBlockPos(AccessControlsMod._LockLocationKey);
@@ -41,7 +42,7 @@ namespace FirstMachineAge
4142 internal static void WriteACL_ItemStack(ref ItemStack targetStack, AccessControlNode acn, BlockPos location)
4243 {
4344 targetStack.Attributes.SetInt(AccessControlsMod._KeyIDKey, acn.KeyID.Value);
44- targetStack.Attributes.SetBlockPos(AccessControlsMod._LockLocationKey, location);
45+ targetStack.Attributes.SetBlockPos(AccessControlsMod._LockLocationKey, location.Copy());
4546 targetStack.Attributes.SetString(AccessControlsMod._itemDescription, acn.NameOfLock);
4647 }
4748
@@ -51,13 +52,16 @@ namespace FirstMachineAge
5152 base.GetHeldItemInfo(inSlot, dsc, world, withDebugInfo);
5253
5354 #if DEBUG
54- int keyId = KeyID(inSlot.Itemstack);
55+ int? keyId = KeyID(inSlot.Itemstack);
5556 dsc.AppendFormat("\nKey #{0:D},", keyId);
5657 #endif
5758
5859
5960 string desc = Description(inSlot.Itemstack);
60- dsc.AppendFormat("\n' {0} '", desc);
61+ if (!string.IsNullOrWhiteSpace(desc)) {
62+ dsc.AppendFormat("\n'{0}'", desc);
63+ }
64+
6165 }
6266
6367
--- a/AccessControls/items/GenericLock.cs
+++ b/AccessControls/items/GenericLock.cs
@@ -60,8 +60,6 @@ namespace FirstMachineAge
6060 this.Logger = this.ClientAPI.World.Logger;
6161 AccessControlsMod = ClientAPI.World.Api.ModLoader.GetModSystem<AccessControlsMod>( );
6262 }
63-
64- Logger.VerboseDebug("{0} ~ OnLoaded", base.Code.ToString());
6563 }
6664
6765
@@ -104,12 +102,12 @@ namespace FirstMachineAge
104102 handling = EnumHandHandling.NotHandled;
105103 }
106104
107- //or? OnCreatedByCrafting -- generate keyID and/or combo (also when getting inv. from a packet) [CLIENTSIDE?!]
105+ //or? OnCreatedByCrafting -- generate keyID and/or combo (also when getting inv. from a packet)
108106 public override void OnModifiedInInventorySlot(IWorldAccessor world, ItemSlot slot, ItemStack extractedStack = null)
109107 {
110108 if (world.Side.IsServer( ))
111109 {
112- //Set keyid,combo if unset...
110+ //should capture creat.inv. cases & trade...TODO: check crafting
113111 #if DEBUG
114112 Logger.VerboseDebug("GenericLock: OnModifiedInInventorySlot ID:{0}", slot.Itemstack.Id);
115113 #endif
@@ -124,12 +122,9 @@ namespace FirstMachineAge
124122 }
125123 else if (this.LockStyle == LockKinds.Key)
126124 {
127- //var keyId = KeyID(slot);
128-
129- //if (keyId.HasValue == false)
130- //{
131- //GenerateKeyId(slot, this);
132- //}
125+ if (this.KeyID(slot).HasValue == false) {
126+ GenerateKeyId(slot, this);
127+ }
133128 }
134129
135130 }
@@ -200,7 +195,7 @@ namespace FirstMachineAge
200195 if (sourceSlot.Itemstack.Attributes.HasAttribute(AccessControlsMod._KeyIDKey)) {
201196 return sourceSlot.Itemstack.Attributes.GetInt(AccessControlsMod._KeyIDKey);
202197 } else {
203- return new int( );
198+ return null;
204199 }
205200 }
206201 }
--- a/AccessControls/items/ItemCombolock.cs
+++ b/AccessControls/items/ItemCombolock.cs
@@ -40,8 +40,6 @@ namespace FirstMachineAge
4040 handling = EnumHandHandling.PreventDefault;
4141 return;
4242 }
43-
44- base.OnHeldInteractStart(slot, byEntity, blockSel, entitySel, firstEvent, ref handling);
4543 }
4644 }
4745 }
--- a/AccessControls/items/ItemKeylock.cs
+++ b/AccessControls/items/ItemKeylock.cs
@@ -13,16 +13,17 @@ namespace FirstMachineAge
1313
1414 public override void OnHeldInteractStart(ItemSlot slot, EntityAgent byEntity, BlockSelection blockSel, EntitySelection entitySel, bool firstEvent, ref EnumHandHandling handling)
1515 {
16+
1617 if (byEntity.World.Side.IsClient( )) {
1718 ClientAPI = (byEntity.World.Api as ICoreClientAPI);
1819 }
1920
20- if (blockSel != null && byEntity.World.BlockAccessor.GetBlock(blockSel.Position).HasBehavior<BlockBehaviorLockable>( )) {
21+ if (blockSel != null && byEntity.World.BlockAccessor.GetBlock(blockSel.Position).HasBehavior<BlockBehaviorComplexLockable>( )) {
2122 IPlayer player = (byEntity as EntityPlayer).Player;
2223
2324
24- if (AccessControlsMod.LockState(blockSel.Position, player) != LockStatus.None)//already has a lock...?
25- {
25+ if (AccessControlsMod.LockState(blockSel.Position, player) != LockStatus.None)//already has a lock...
26+ {//TODO: Add Lock owner text
2627 ClientAPI?.TriggerIngameError(this, "cannotlock", Lang.Get("ingameerror-cannotlock"));
2728 }
2829 else {
@@ -32,15 +33,12 @@ namespace FirstMachineAge
3233 ClientAPI?.ShowChatMessage(Lang.Get("lockapplied"));
3334 slot.TakeOut(1);
3435 slot.MarkDirty( );
35- //TODO: after create matching KEY for lock - make this slot highlighted for that key??
36-
3736 }
3837
3938 handling = EnumHandHandling.PreventDefault;
4039 return;
4140 }
42-
43- base.OnHeldInteractStart(slot, byEntity, blockSel, entitySel, firstEvent, ref handling);
41+
4442 }
4543 }
4644 }
--- a/FirstMachineAge_Common/Helpers.cs
+++ b/FirstMachineAge_Common/Helpers.cs
@@ -191,7 +191,7 @@ namespace FirstMachineAge
191191
192192 public static void SetBlockPos(this ITreeAttribute source, string key, BlockPos value)
193193 {
194- byte[] buffer = new byte[3];
194+ byte[] buffer = new byte[12];
195195
196196 using (MemoryStream bytesStream = new MemoryStream(buffer,true))
197197 {