hytopia 0.1.77 → 0.1.79
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/server.entity.despawn.md +1 -1
- package/docs/server.entity.md +44 -2
- package/docs/server.entity.parent.md +13 -0
- package/docs/server.entity.parentnodename.md +13 -0
- package/docs/server.entity.spawn.md +19 -3
- package/docs/server.entitymanager.getentitychildren.md +55 -0
- package/docs/server.entitymanager.md +14 -0
- package/docs/server.entityoptions.md +38 -0
- package/docs/server.entityoptions.parent.md +13 -0
- package/docs/server.entityoptions.parentnodename.md +13 -0
- package/docs/server.gameserver.md +2 -2
- package/docs/{server.gameserver.modelmanager.md → server.gameserver.modelregistry.md} +3 -3
- package/docs/server.md +1 -1
- package/docs/{server.modelmanager.getboundingbox.md → server.modelregistry.getboundingbox.md} +2 -2
- package/docs/server.modelregistry.getnodenames.md +55 -0
- package/docs/server.modelregistry.instance.md +13 -0
- package/docs/server.modelregistry.md +139 -0
- package/docs/server.modelregistry.modelhasnode.md +71 -0
- package/examples/ai-agents/README.md +47 -0
- package/examples/ai-agents/assets/map.json +25828 -0
- package/examples/ai-agents/assets/ui/index.html +215 -0
- package/examples/ai-agents/index.ts +350 -0
- package/examples/ai-agents/package.json +16 -0
- package/examples/ai-agents/src/BaseAgent.ts +482 -0
- package/examples/ai-agents/src/behaviors/FishingBehavior.ts +181 -0
- package/examples/ai-agents/src/behaviors/FollowBehavior.ts +171 -0
- package/examples/ai-agents/src/behaviors/MiningBehavior.ts +226 -0
- package/examples/ai-agents/src/behaviors/PathfindingBehavior.ts +435 -0
- package/examples/ai-agents/src/behaviors/SpeakBehavior.ts +50 -0
- package/examples/ai-agents/src/behaviors/TradeBehavior.ts +254 -0
- package/examples/entity-controller/MyEntityController.ts +1 -1
- package/package.json +1 -1
- package/server.api.json +324 -21
- package/server.d.ts +47 -22
- package/server.js +83 -83
- package/docs/server.modelmanager.instance.md +0 -13
- package/docs/server.modelmanager.md +0 -111
@@ -0,0 +1,254 @@
|
|
1
|
+
import { World } from "hytopia";
|
2
|
+
import { BaseAgent, type AgentBehavior } from "../BaseAgent";
|
3
|
+
|
4
|
+
interface TradeRequest {
|
5
|
+
from: BaseAgent;
|
6
|
+
to: BaseAgent;
|
7
|
+
offerItems: { name: string; quantity: number }[];
|
8
|
+
requestItems: { name: string; quantity: number }[];
|
9
|
+
timestamp: number;
|
10
|
+
}
|
11
|
+
/**
|
12
|
+
* Simple implementation of trade behavior for Agents.
|
13
|
+
* This is a simple example of how behaviours can have state, in this case, a map of active trade requests.
|
14
|
+
* It also demonstrates how Behaviors can manage agent state like the inventory.
|
15
|
+
*/
|
16
|
+
export class TradeBehavior implements AgentBehavior {
|
17
|
+
private activeRequests: Map<string, TradeRequest> = new Map();
|
18
|
+
|
19
|
+
onUpdate(agent: BaseAgent, world: World): void {}
|
20
|
+
|
21
|
+
private generateTradeId(from: BaseAgent, to: BaseAgent): string {
|
22
|
+
return `${from.name}_${to.name}_${Date.now()}`;
|
23
|
+
}
|
24
|
+
|
25
|
+
onToolCall(
|
26
|
+
agent: BaseAgent,
|
27
|
+
world: World,
|
28
|
+
toolName: string,
|
29
|
+
args: any
|
30
|
+
): string | void {
|
31
|
+
if (toolName === "request_trade") {
|
32
|
+
const { target, offer, request } = args;
|
33
|
+
|
34
|
+
// Find target agent
|
35
|
+
const nearbyEntities = agent.getNearbyEntities(5);
|
36
|
+
const targetEntity = nearbyEntities.find((e) => e.name === target);
|
37
|
+
|
38
|
+
if (!targetEntity || targetEntity.type !== "Agent") {
|
39
|
+
return `Cannot find ${target} nearby to trade with.`;
|
40
|
+
}
|
41
|
+
|
42
|
+
const targetAgent = world.entityManager
|
43
|
+
.getAllEntities()
|
44
|
+
.find(
|
45
|
+
(e) => e instanceof BaseAgent && e.name === target
|
46
|
+
) as BaseAgent;
|
47
|
+
|
48
|
+
if (!targetAgent) return "Target agent not found";
|
49
|
+
|
50
|
+
// Verify agent has the items they're offering
|
51
|
+
for (const item of offer) {
|
52
|
+
if (!agent.removeFromInventory(item.name, item.quantity)) {
|
53
|
+
return `You don't have enough ${item.name} to offer.`;
|
54
|
+
}
|
55
|
+
// Return items since this is just a check
|
56
|
+
agent.addToInventory({
|
57
|
+
name: item.name,
|
58
|
+
quantity: item.quantity,
|
59
|
+
});
|
60
|
+
}
|
61
|
+
|
62
|
+
const tradeId = this.generateTradeId(agent, targetAgent);
|
63
|
+
const tradeRequest = {
|
64
|
+
from: agent,
|
65
|
+
to: targetAgent,
|
66
|
+
offerItems: offer,
|
67
|
+
requestItems: request,
|
68
|
+
timestamp: Date.now(),
|
69
|
+
};
|
70
|
+
|
71
|
+
// Set trade request in both agents' trade behaviors
|
72
|
+
this.activeRequests.set(tradeId, tradeRequest);
|
73
|
+
const targetTradeBehavior = targetAgent
|
74
|
+
.getBehaviors()
|
75
|
+
.find((b) => b instanceof TradeBehavior) as TradeBehavior;
|
76
|
+
if (targetTradeBehavior) {
|
77
|
+
targetTradeBehavior.activeRequests.set(tradeId, tradeRequest);
|
78
|
+
}
|
79
|
+
|
80
|
+
targetAgent.handleEnvironmentTrigger(
|
81
|
+
`${agent.name} wants to trade!\n` +
|
82
|
+
`Offering: ${offer
|
83
|
+
.map(
|
84
|
+
(i: { quantity: number; name: string }) =>
|
85
|
+
`${i.quantity}x ${i.name}`
|
86
|
+
)
|
87
|
+
.join(", ")}\n` +
|
88
|
+
`Requesting: ${request
|
89
|
+
.map(
|
90
|
+
(i: { quantity: number; name: string }) =>
|
91
|
+
`${i.quantity}x ${i.name}`
|
92
|
+
)
|
93
|
+
.join(", ")}\n` +
|
94
|
+
`Use accept_trade or decline_trade with tradeId: ${tradeId}`
|
95
|
+
);
|
96
|
+
|
97
|
+
return "Trade request sent!";
|
98
|
+
} else if (toolName === "accept_trade") {
|
99
|
+
const { tradeId } = args;
|
100
|
+
const request = this.activeRequests.get(tradeId);
|
101
|
+
console.log(`${agent.name} attempting to accept trade ${tradeId}`);
|
102
|
+
|
103
|
+
if (!request) {
|
104
|
+
console.log(`Trade ${tradeId} not found or expired`);
|
105
|
+
return "Trade request not found or expired.";
|
106
|
+
}
|
107
|
+
|
108
|
+
if (request.to !== agent) {
|
109
|
+
console.log(
|
110
|
+
`${agent.name} tried to accept trade meant for ${request.to.name}`
|
111
|
+
);
|
112
|
+
return "This trade request was not sent to you.";
|
113
|
+
}
|
114
|
+
|
115
|
+
// Verify receiving agent has requested items
|
116
|
+
for (const item of request.requestItems) {
|
117
|
+
if (!agent.removeFromInventory(item.name, item.quantity)) {
|
118
|
+
console.log(
|
119
|
+
`${agent.name} lacks required item: ${item.quantity}x ${item.name}`
|
120
|
+
);
|
121
|
+
return `You don't have enough ${item.name} to complete the trade.`;
|
122
|
+
}
|
123
|
+
|
124
|
+
// Return items since this is just a check
|
125
|
+
agent.addToInventory({
|
126
|
+
name: item.name,
|
127
|
+
quantity: item.quantity,
|
128
|
+
});
|
129
|
+
}
|
130
|
+
|
131
|
+
console.log(
|
132
|
+
`Executing trade between ${request.from.name} and ${request.to.name}`
|
133
|
+
);
|
134
|
+
console.log(
|
135
|
+
`${request.from.name} offers: ${JSON.stringify(
|
136
|
+
request.offerItems
|
137
|
+
)}`
|
138
|
+
);
|
139
|
+
console.log(
|
140
|
+
`${request.to.name} offers: ${JSON.stringify(
|
141
|
+
request.requestItems
|
142
|
+
)}`
|
143
|
+
);
|
144
|
+
|
145
|
+
// Execute the trade
|
146
|
+
// Remove items from both agents
|
147
|
+
for (const item of request.offerItems) {
|
148
|
+
request.from.removeFromInventory(item.name, item.quantity);
|
149
|
+
}
|
150
|
+
for (const item of request.requestItems) {
|
151
|
+
request.to.removeFromInventory(item.name, item.quantity);
|
152
|
+
}
|
153
|
+
|
154
|
+
// Add items to both agents
|
155
|
+
for (const item of request.offerItems) {
|
156
|
+
request.to.addToInventory({
|
157
|
+
name: item.name,
|
158
|
+
quantity: item.quantity,
|
159
|
+
});
|
160
|
+
}
|
161
|
+
for (const item of request.requestItems) {
|
162
|
+
request.from.addToInventory({
|
163
|
+
name: item.name,
|
164
|
+
quantity: item.quantity,
|
165
|
+
});
|
166
|
+
}
|
167
|
+
|
168
|
+
// Clear trade request from both agents' trade behaviors
|
169
|
+
this.activeRequests.delete(tradeId);
|
170
|
+
const fromTradeBehavior = request.from
|
171
|
+
.getBehaviors()
|
172
|
+
.find((b) => b instanceof TradeBehavior) as TradeBehavior;
|
173
|
+
if (fromTradeBehavior) {
|
174
|
+
fromTradeBehavior.activeRequests.delete(tradeId);
|
175
|
+
}
|
176
|
+
|
177
|
+
console.log(`Trade ${tradeId} completed successfully`);
|
178
|
+
|
179
|
+
// Notify both agents
|
180
|
+
request.from.handleEnvironmentTrigger(
|
181
|
+
`${agent.name} accepted your trade!`
|
182
|
+
);
|
183
|
+
request.to.handleEnvironmentTrigger(
|
184
|
+
`You accepted the trade with ${agent.name}!`
|
185
|
+
);
|
186
|
+
return "Trade completed successfully!";
|
187
|
+
} else if (toolName === "decline_trade") {
|
188
|
+
const { tradeId } = args;
|
189
|
+
const request = this.activeRequests.get(tradeId);
|
190
|
+
|
191
|
+
if (!request) {
|
192
|
+
console.log("Trade request not found or expired.");
|
193
|
+
return "Trade request not found or expired.";
|
194
|
+
}
|
195
|
+
|
196
|
+
if (request.to !== agent) {
|
197
|
+
console.log("This trade request was not sent to you.");
|
198
|
+
return "This trade request was not sent to you.";
|
199
|
+
}
|
200
|
+
|
201
|
+
// Clear trade request from both agents' trade behaviors
|
202
|
+
this.activeRequests.delete(tradeId);
|
203
|
+
const fromTradeBehavior = request.from
|
204
|
+
.getBehaviors()
|
205
|
+
.find((b) => b instanceof TradeBehavior) as TradeBehavior;
|
206
|
+
if (fromTradeBehavior) {
|
207
|
+
fromTradeBehavior.activeRequests.delete(tradeId);
|
208
|
+
}
|
209
|
+
|
210
|
+
request.from.handleEnvironmentTrigger(
|
211
|
+
`${agent.name} declined your trade.`
|
212
|
+
);
|
213
|
+
console.log("Trade declined.");
|
214
|
+
return "Trade declined.";
|
215
|
+
}
|
216
|
+
}
|
217
|
+
|
218
|
+
getPromptInstructions(): string {
|
219
|
+
return `
|
220
|
+
To request a trade with another agent:
|
221
|
+
<action type="request_trade">
|
222
|
+
{
|
223
|
+
"target": "name of agent to trade with",
|
224
|
+
"offer": [{ "name": "item name", "quantity": number }],
|
225
|
+
"request": [{ "name": "item name", "quantity": number }]
|
226
|
+
}
|
227
|
+
</action>
|
228
|
+
|
229
|
+
To accept a trade request:
|
230
|
+
<action type="accept_trade">
|
231
|
+
{
|
232
|
+
"tradeId": "trade_id_from_request"
|
233
|
+
}
|
234
|
+
</action>
|
235
|
+
|
236
|
+
To decline a trade request:
|
237
|
+
<action type="decline_trade">
|
238
|
+
{
|
239
|
+
"tradeId": "trade_id_from_request"
|
240
|
+
}
|
241
|
+
</action>
|
242
|
+
|
243
|
+
Trading requires both agents to be within 5 meters of each other.
|
244
|
+
Both agents must have the required items in their inventory.
|
245
|
+
|
246
|
+
If someone verbally offers a trade, but you don't get the official request from the Environment, you should ask them to request the trade so you can accept it.
|
247
|
+
|
248
|
+
If you request to trade with an agent, they will need to accept the trade request.`;
|
249
|
+
}
|
250
|
+
|
251
|
+
getState(): string {
|
252
|
+
return `Active trade requests: ${JSON.stringify(this.activeRequests)}`;
|
253
|
+
}
|
254
|
+
}
|
@@ -144,7 +144,7 @@ export default class MyEntityController extends BaseEntityController {
|
|
144
144
|
collidesWith: [ CollisionGroup.BLOCK, CollisionGroup.ENTITY ],
|
145
145
|
},
|
146
146
|
isSensor: true,
|
147
|
-
|
147
|
+
position: { x: 0, y: -0.75, z: 0 },
|
148
148
|
tag: 'groundSensor',
|
149
149
|
onCollision: (_other: BlockType | Entity, started: boolean) => {
|
150
150
|
// Ground contact
|