Hosted rooms: seats, owner-first room IDs, T5 items in their scenes
CI / build (push) Successful in 31s
CI / build (push) Successful in 31s
- The seat chosen on joining, and "*msg SeatAssignment" room seats, go to the hub, so people joining get free seats (both stood on seat 1). - Instance IDs are "<owner>-<2100000000+n>" (the hub's change): the client takes the owner from them, so owners can decorate their rooms. - A hosted room's scene carries community products as the hub's IDs (placements too), turned into local IDs by each proxy, instead of being stripped; saved and loaded scenes translate placements too. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
13ee023836
commit
c483099558
6 files changed
+192
-31
No files matched your search
@@ -17,6 +17,7 @@ import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -135,7 +136,9 @@ func roomOwner(instance string) int64 {
|
||||
|
||||
// isHostedInstance reports whether a room is hosted by the hub (its scene is the hosted package's).
|
||||
func isHostedInstance(instance string) bool {
|
||||
return strings.HasPrefix(instance, strconv.Itoa(HostedOwnerID)+"-")
|
||||
_, number, ok := strings.Cut(instance, "-")
|
||||
n, err := strconv.ParseInt(number, 10, 64)
|
||||
return ok && err == nil && n > HostedOwnerID // hosted rooms' numbers start above 2100000000
|
||||
}
|
||||
|
||||
// decorFetch gets a room's decorations, from memory or the hub.
|
||||
@@ -452,6 +455,16 @@ func (p *Products) outgoingScene(m *chatMessage) ([]byte, bool) {
|
||||
}
|
||||
holder, rows := sceneContents(scene)
|
||||
instance := sceneInstance(scene)
|
||||
if isHostedInstance(instance) {
|
||||
// A hosted room's scene goes only to T5DE users (through the hub): its community products travel as the
|
||||
// hub's product IDs, which each proxy turns into its own local IDs.
|
||||
out, changed := p.globalScene(rows)
|
||||
if !changed {
|
||||
return nil, false
|
||||
}
|
||||
holder["room_contents"] = out
|
||||
return m.withText(setRoomStateCommand + encodeScene(scene)), true
|
||||
}
|
||||
rest, items, stripped := p.splitCommunity(rows)
|
||||
_, hadDecor := p.decor.get(instance)
|
||||
if instance != "" && !isHostedInstance(instance) && (stripped > 0 || hadDecor) {
|
||||
@@ -544,7 +557,16 @@ func (p *Products) incomingScene(m *chatMessage) ([]byte, bool) {
|
||||
// decorateScene adds the room's decorations to a scene IMVU sent: the scene encoded again, and whether it changed.
|
||||
func (p *Products) decorateScene(scene map[string]any) (string, bool) {
|
||||
instance := sceneInstance(scene)
|
||||
if instance == "" || isHostedInstance(instance) {
|
||||
if isHostedInstance(instance) {
|
||||
holder, rows := sceneContents(scene)
|
||||
out, changed := p.localScene(rows)
|
||||
if !changed {
|
||||
return "", false
|
||||
}
|
||||
holder["room_contents"] = out
|
||||
return encodeScene(scene), true
|
||||
}
|
||||
if instance == "" {
|
||||
return "", false
|
||||
}
|
||||
holder, rows := sceneContents(scene)
|
||||
@@ -610,3 +632,58 @@ func (p *Products) DecorChanged(instance string) {
|
||||
}
|
||||
p.logf("decor: gave the client %s again with its decorations", instance)
|
||||
}
|
||||
|
||||
// globalScene gives a hosted room's community products as the hub's product IDs (in the row and its placement); a
|
||||
// local pack's can't be shared and is left out.
|
||||
func (p *Products) globalScene(rows []any) ([]any, bool) {
|
||||
out, changed := make([]any, 0, len(rows)), false
|
||||
for _, row := range rows {
|
||||
r, ok := row.([]any)
|
||||
product, isNumber := int64(0), false
|
||||
if ok && len(r) >= 3 {
|
||||
product, isNumber = numberOf(r[1])
|
||||
}
|
||||
if !isNumber || !IsCommunity(product) {
|
||||
out = append(out, row)
|
||||
continue
|
||||
}
|
||||
changed = true
|
||||
id, ok := p.state.productID(product)
|
||||
if !ok || !p.inHubDir(id) {
|
||||
continue
|
||||
}
|
||||
placement, _ := r[2].(string)
|
||||
out = append(out, []any{r[0], id, swapToken(placement, strconv.FormatInt(product, 10), id)})
|
||||
}
|
||||
return out, changed
|
||||
}
|
||||
|
||||
// localScene turns hub product IDs in a hosted room's scene into local IDs (asking for products that aren't here).
|
||||
func (p *Products) localScene(rows []any) ([]any, bool) {
|
||||
out, changed := make([]any, 0, len(rows)), false
|
||||
for _, row := range rows {
|
||||
r, ok := row.([]any)
|
||||
if !ok || len(r) < 3 {
|
||||
out = append(out, row)
|
||||
continue
|
||||
}
|
||||
id, isString := r[1].(string)
|
||||
if !isString || !hubIDPattern.MatchString(id) {
|
||||
out = append(out, row)
|
||||
continue
|
||||
}
|
||||
changed = true
|
||||
local, ok := p.LocalID(id)
|
||||
if !ok {
|
||||
if p.Want != nil {
|
||||
p.Want(id)
|
||||
}
|
||||
continue
|
||||
}
|
||||
placement, _ := r[2].(string)
|
||||
out = append(out, []any{r[0], local, swapToken(placement, id, strconv.FormatInt(local, 10))})
|
||||
}
|
||||
return out, changed
|
||||
}
|
||||
|
||||
var hubIDPattern = regexp.MustCompile(`^[a-z2-7]{10}$`)
|
||||
@@ -144,3 +144,30 @@ func TestDecorationsInServerRoomState(t *testing.T) {
|
||||
t.Fatalf("delivered %s", in)
|
||||
}
|
||||
}
|
||||
|
||||
// In a hosted room, community products travel as the hub's IDs and come back as local IDs.
|
||||
func TestHostedRoomScene(t *testing.T) {
|
||||
h, _ := newHarness(t)
|
||||
hubDir := t.TempDir()
|
||||
writePack(t, hubDir, "sofasofa22", 1)
|
||||
h.p.dirs = append(h.p.dirs, hubDir)
|
||||
h.p.HubDir = hubDir
|
||||
h.p.refresh(true)
|
||||
sofa := h.p.byProductID("sofasofa22").local
|
||||
scene := map[string]any{"room_state": map[string]any{
|
||||
"room_info": map[string]any{"room_instance_id": "7-2100000003"},
|
||||
"room_contents": []any{[]any{10, 301, "2-1"}, []any{11, sofa, fmt.Sprintf("3 0 11 %d 6", sofa)}}}}
|
||||
msg, _ := json.Marshal(map[string]any{"userId": 7, "message": setRoomStateCommand + encodeScene(scene)})
|
||||
out, _ := h.p.outgoing("/chat/2100000003", "messages", msg)
|
||||
m, _ := parseChatMessage(out)
|
||||
sent, _ := decodeScene(strings.TrimPrefix(m.text, setRoomStateCommand))
|
||||
if _, rows := sceneContents(sent); fmt.Sprint(rows) != "[[10 301 2-1] [11 sofasofa22 3 0 11 sofasofa22 6]]" {
|
||||
t.Fatalf("sent %v", rows)
|
||||
}
|
||||
in := h.p.Incoming(nil, imq.Delivered{UserID: "7", Queue: "/chat/2100000003", Mount: "messages", Message: out})
|
||||
m, _ = parseChatMessage(in)
|
||||
got, _ := decodeScene(strings.TrimPrefix(m.text, setRoomStateCommand))
|
||||
if _, rows := sceneContents(got); fmt.Sprint(rows) != fmt.Sprintf("[[10 301 2-1] [11 %d 3 0 11 %d 6]]", sofa, sofa) {
|
||||
t.Fatalf("delivered %v", rows)
|
||||
}
|
||||
}
|
||||
@@ -571,7 +571,7 @@ func (p *Products) interceptBaseLook(x *proxy.Exchange) error {
|
||||
func (p *Products) Outgoing(queue, mount string, message []byte) ([]byte, bool) {
|
||||
out, drop := p.outgoing(queue, mount, message)
|
||||
// A hosted room's message goes to the hub (without community products, as to IMVU), never to IMVU.
|
||||
if p.Hosted != nil && !drop && p.Hosted.Send(queue, mount, out) {
|
||||
if p.Hosted != nil && p.Hosted.Send(queue, mount, out, message, drop) {
|
||||
return nil, true
|
||||
}
|
||||
return out, drop
|
||||
|
||||
@@ -115,10 +115,11 @@ func (p *Products) Ready(id string) {
|
||||
const HostedOwnerID = 2100000000
|
||||
|
||||
// HostedRooms serves T5DE-hosted rooms' IMQ queues: Route answers subscriptions to them, Send takes the client's
|
||||
// messages to them (reporting whether the queue was one), Closed forgets a connection.
|
||||
// messages to them (message as for others, original as the client sent it, for the echo; keep: nothing to send
|
||||
// others), reporting whether the queue was one; Closed forgets a connection.
|
||||
type HostedRooms interface {
|
||||
Route(c *proxy.IMQConn, unsubscribe bool, s imq.Subscription) imq.Subscription
|
||||
Send(queue, mount string, message []byte) bool
|
||||
Send(queue, mount string, message, original []byte, keep bool) bool
|
||||
Closed(c *proxy.IMQConn)
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user