diff options
-rw-r--r-- | api/database/database.go | 2 | ||||
-rw-r--r-- | api/database/models.go | 2 | ||||
-rw-r--r-- | api/handlers/handlers.go | 14 | ||||
-rw-r--r-- | cmd/api/main.go | 2 | ||||
-rw-r--r-- | network/ip.go | 33 | ||||
-rw-r--r-- | network/network.go | 27 | ||||
-rw-r--r-- | network/session.go (renamed from relay/session.go) | 2 | ||||
-rw-r--r-- | relay/proto/Makefile | 6 | ||||
-rw-r--r-- | relay/proto/relay.pb.go | 354 | ||||
-rw-r--r-- | relay/proto/relay.proto | 31 | ||||
-rw-r--r-- | relay/proto/relay_grpc.pb.go | 177 | ||||
-rw-r--r-- | relay/relay.go | 79 | ||||
-rw-r--r-- | relay/relay_test.go | 86 | ||||
-rw-r--r-- | ui/views/play.go | 42 |
14 files changed, 65 insertions, 792 deletions
diff --git a/api/database/database.go b/api/database/database.go index 0ba40aa..4470c58 100644 --- a/api/database/database.go +++ b/api/database/database.go @@ -17,7 +17,7 @@ func InitDb(dsn string) (*gorm.DB, error) { db, err = gorm.Open(postgres.Open(dsn), &gorm.Config{}) if err == nil { - db.AutoMigrate(&User{}, &Play{}) + db.AutoMigrate(&User{}, &Game{}) } return db, err diff --git a/api/database/models.go b/api/database/models.go index 900c7b3..a6e76c5 100644 --- a/api/database/models.go +++ b/api/database/models.go @@ -10,7 +10,7 @@ type User struct { UpdatedAt time.Time `json:"updated_at"` } -type Play struct { +type Game struct { ID int `json:"id"` Player1ID int `json:"-"` Player1 User `gorm:"foreignKey:Player1ID" json:"player1"` diff --git a/api/handlers/handlers.go b/api/handlers/handlers.go index fe2ca58..601b770 100644 --- a/api/handlers/handlers.go +++ b/api/handlers/handlers.go @@ -8,13 +8,13 @@ import ( "github.com/boozec/rahanna/api/auth" "github.com/boozec/rahanna/api/database" + "github.com/boozec/rahanna/network" utils "github.com/boozec/rahanna/pkg" - "github.com/boozec/rahanna/relay" "golang.org/x/crypto/bcrypt" "gorm.io/gorm" ) -type NewPlayRequest struct { +type NewGameRequest struct { IP string `json:"ip"` } @@ -121,8 +121,8 @@ func NewPlay(w http.ResponseWriter, r *http.Request) { db, _ := database.GetDb() - name := relay.NewSession() - play := database.Play{ + name := network.NewSession() + play := database.Game{ Player1ID: claims.UserID, Player2ID: nil, Name: name, @@ -139,8 +139,8 @@ func NewPlay(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(map[string]string{"name": name}) } -func EnterPlay(w http.ResponseWriter, r *http.Request) { - slog.Info("POST /enter-play") +func EnterGame(w http.ResponseWriter, r *http.Request) { + slog.Info("POST /enter-game") claims, err := auth.ValidateJWT(r.Header.Get("Authorization")) if err != nil { @@ -165,7 +165,7 @@ func EnterPlay(w http.ResponseWriter, r *http.Request) { db, _ := database.GetDb() - var play database.Play + var play database.Game result := db.Where("name = ? AND player2_id IS NULL", payload.Name).First(&play) if result.Error != nil { diff --git a/cmd/api/main.go b/cmd/api/main.go index b1f912f..96f7a2f 100644 --- a/cmd/api/main.go +++ b/cmd/api/main.go @@ -19,7 +19,7 @@ func main() { r.HandleFunc("/auth/register", handlers.RegisterUser).Methods(http.MethodPost) r.HandleFunc("/auth/login", handlers.LoginUser).Methods(http.MethodPost) r.Handle("/play", middleware.AuthMiddleware(http.HandlerFunc(handlers.NewPlay))).Methods(http.MethodPost) - r.Handle("/enter-play", middleware.AuthMiddleware(http.HandlerFunc(handlers.EnterPlay))).Methods(http.MethodPost) + r.Handle("/enter-game", middleware.AuthMiddleware(http.HandlerFunc(handlers.EnterGame))).Methods(http.MethodPost) slog.Info("Serving on :8080") handler := cors.AllowAll().Handler(r) diff --git a/network/ip.go b/network/ip.go new file mode 100644 index 0000000..0c6451e --- /dev/null +++ b/network/ip.go @@ -0,0 +1,33 @@ +package network + +import ( + "fmt" + "log/slog" + "math/rand" + "net" +) + +func GetOutboundIP() net.IP { + conn, err := net.Dial("udp", "8.8.8.8:80") + if err != nil { + slog.Error("err", err) + } + defer conn.Close() + + localAddr := conn.LocalAddr().(*net.UDPAddr) + + return localAddr.IP +} + +func GetRandomAvailablePort() (int, error) { + for i := 0; i < 100; i += 1 { + port := rand.Intn(65535-1024) + 1024 + addr := fmt.Sprintf(":%d", port) + ln, err := net.Listen("tcp", addr) + if err == nil { + defer ln.Close() + return port, nil + } + } + return 0, fmt.Errorf("failed to find an available port after multiple attempts") +} diff --git a/network/network.go b/network/network.go index 5ab2dac..8283993 100644 --- a/network/network.go +++ b/network/network.go @@ -4,8 +4,6 @@ import ( "bufio" "encoding/json" "fmt" - "log/slog" - "math/rand" "net" "sync" "time" @@ -213,28 +211,3 @@ func (n *TCPNetwork) IsConnected() bool { defer n.Unlock() return n.isConnected } - -func GetOutboundIP() net.IP { - conn, err := net.Dial("udp", "8.8.8.8:80") - if err != nil { - slog.Error("err", err) - } - defer conn.Close() - - localAddr := conn.LocalAddr().(*net.UDPAddr) - - return localAddr.IP -} - -func GetRandomAvailablePort() (int, error) { - for i := 0; i < 100; i += 1 { - port := rand.Intn(65535-1024) + 1024 - addr := fmt.Sprintf(":%d", port) - ln, err := net.Listen("tcp", addr) - if err == nil { - defer ln.Close() - return port, nil - } - } - return 0, fmt.Errorf("failed to find an available port after multiple attempts") -} diff --git a/relay/session.go b/network/session.go index a2bc50e..a4f60aa 100644 --- a/relay/session.go +++ b/network/session.go @@ -1,4 +1,4 @@ -package relay +package network import ( "math/rand" diff --git a/relay/proto/Makefile b/relay/proto/Makefile deleted file mode 100644 index fa95be4..0000000 --- a/relay/proto/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -protoc: - protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative relay.proto - -clean: - rm -rf relay.pb.go - rm -rf relay_grpc.pb.go diff --git a/relay/proto/relay.pb.go b/relay/proto/relay.pb.go deleted file mode 100644 index 2f88408..0000000 --- a/relay/proto/relay.pb.go +++ /dev/null @@ -1,354 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.28.1 -// protoc v3.21.12 -// source: relay.proto - -package proto - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type RelayRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Ip string `protobuf:"bytes,1,opt,name=ip,proto3" json:"ip,omitempty"` -} - -func (x *RelayRequest) Reset() { - *x = RelayRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_relay_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *RelayRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RelayRequest) ProtoMessage() {} - -func (x *RelayRequest) ProtoReflect() protoreflect.Message { - mi := &file_relay_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RelayRequest.ProtoReflect.Descriptor instead. -func (*RelayRequest) Descriptor() ([]byte, []int) { - return file_relay_proto_rawDescGZIP(), []int{0} -} - -func (x *RelayRequest) GetIp() string { - if x != nil { - return x.Ip - } - return "" -} - -type LookupRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` -} - -func (x *LookupRequest) Reset() { - *x = LookupRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_relay_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *LookupRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*LookupRequest) ProtoMessage() {} - -func (x *LookupRequest) ProtoReflect() protoreflect.Message { - mi := &file_relay_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use LookupRequest.ProtoReflect.Descriptor instead. -func (*LookupRequest) Descriptor() ([]byte, []int) { - return file_relay_proto_rawDescGZIP(), []int{1} -} - -func (x *LookupRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -type RelayResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Ip string `protobuf:"bytes,2,opt,name=ip,proto3" json:"ip,omitempty"` -} - -func (x *RelayResponse) Reset() { - *x = RelayResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_relay_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *RelayResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RelayResponse) ProtoMessage() {} - -func (x *RelayResponse) ProtoReflect() protoreflect.Message { - mi := &file_relay_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RelayResponse.ProtoReflect.Descriptor instead. -func (*RelayResponse) Descriptor() ([]byte, []int) { - return file_relay_proto_rawDescGZIP(), []int{2} -} - -func (x *RelayResponse) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *RelayResponse) GetIp() string { - if x != nil { - return x.Ip - } - return "" -} - -type CloseResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Status bool `protobuf:"varint,1,opt,name=status,proto3" json:"status,omitempty"` -} - -func (x *CloseResponse) Reset() { - *x = CloseResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_relay_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CloseResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CloseResponse) ProtoMessage() {} - -func (x *CloseResponse) ProtoReflect() protoreflect.Message { - mi := &file_relay_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CloseResponse.ProtoReflect.Descriptor instead. -func (*CloseResponse) Descriptor() ([]byte, []int) { - return file_relay_proto_rawDescGZIP(), []int{3} -} - -func (x *CloseResponse) GetStatus() bool { - if x != nil { - return x.Status - } - return false -} - -var File_relay_proto protoreflect.FileDescriptor - -var file_relay_proto_rawDesc = []byte{ - 0x0a, 0x0b, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x1e, 0x0a, - 0x0c, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, - 0x02, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x70, 0x22, 0x23, 0x0a, - 0x0d, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x22, 0x33, 0x0a, 0x0d, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x70, 0x22, 0x27, 0x0a, 0x0d, 0x43, 0x6c, 0x6f, 0x73, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x32, 0x93, 0x01, 0x0a, 0x05, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x2f, 0x0a, 0x0c, 0x52, 0x65, - 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x0d, 0x2e, 0x52, 0x65, 0x6c, - 0x61, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x52, 0x65, 0x6c, 0x61, - 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x2a, 0x0a, 0x06, 0x4c, - 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x12, 0x0e, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x2d, 0x0a, 0x09, 0x43, 0x6c, 0x6f, 0x73, 0x65, - 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x2e, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x27, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x6f, 0x6f, 0x7a, 0x65, 0x63, 0x2f, 0x72, 0x61, 0x68, 0x61, - 0x6e, 0x6e, 0x61, 0x2f, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_relay_proto_rawDescOnce sync.Once - file_relay_proto_rawDescData = file_relay_proto_rawDesc -) - -func file_relay_proto_rawDescGZIP() []byte { - file_relay_proto_rawDescOnce.Do(func() { - file_relay_proto_rawDescData = protoimpl.X.CompressGZIP(file_relay_proto_rawDescData) - }) - return file_relay_proto_rawDescData -} - -var file_relay_proto_msgTypes = make([]protoimpl.MessageInfo, 4) -var file_relay_proto_goTypes = []interface{}{ - (*RelayRequest)(nil), // 0: RelayRequest - (*LookupRequest)(nil), // 1: LookupRequest - (*RelayResponse)(nil), // 2: RelayResponse - (*CloseResponse)(nil), // 3: CloseResponse -} -var file_relay_proto_depIdxs = []int32{ - 0, // 0: Relay.RegisterName:input_type -> RelayRequest - 1, // 1: Relay.Lookup:input_type -> LookupRequest - 1, // 2: Relay.CloseName:input_type -> LookupRequest - 2, // 3: Relay.RegisterName:output_type -> RelayResponse - 2, // 4: Relay.Lookup:output_type -> RelayResponse - 3, // 5: Relay.CloseName:output_type -> CloseResponse - 3, // [3:6] is the sub-list for method output_type - 0, // [0:3] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name -} - -func init() { file_relay_proto_init() } -func file_relay_proto_init() { - if File_relay_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_relay_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RelayRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_relay_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LookupRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_relay_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RelayResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_relay_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CloseResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_relay_proto_rawDesc, - NumEnums: 0, - NumMessages: 4, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_relay_proto_goTypes, - DependencyIndexes: file_relay_proto_depIdxs, - MessageInfos: file_relay_proto_msgTypes, - }.Build() - File_relay_proto = out.File - file_relay_proto_rawDesc = nil - file_relay_proto_goTypes = nil - file_relay_proto_depIdxs = nil -} diff --git a/relay/proto/relay.proto b/relay/proto/relay.proto deleted file mode 100644 index cbad184..0000000 --- a/relay/proto/relay.proto +++ /dev/null @@ -1,31 +0,0 @@ -syntax = "proto3"; - -option go_package = "github.com/boozec/rahanna/relay/proto"; - -service Relay -{ - rpc RegisterName(RelayRequest) returns (RelayResponse) {} - rpc Lookup(LookupRequest) returns (RelayResponse) {} - rpc CloseName(LookupRequest) returns (CloseResponse) {} -} - -message RelayRequest -{ - string ip = 1; -} - -message LookupRequest -{ - string name = 1; -} - -message RelayResponse -{ - string name = 1; - string ip = 2; -} - -message CloseResponse -{ - bool status = 1; -} diff --git a/relay/proto/relay_grpc.pb.go b/relay/proto/relay_grpc.pb.go deleted file mode 100644 index a102e96..0000000 --- a/relay/proto/relay_grpc.pb.go +++ /dev/null @@ -1,177 +0,0 @@ -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. -// versions: -// - protoc-gen-go-grpc v1.2.0 -// - protoc v3.21.12 -// source: relay.proto - -package proto - -import ( - context "context" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" -) - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 - -// RelayClient is the client API for Relay service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type RelayClient interface { - RegisterName(ctx context.Context, in *RelayRequest, opts ...grpc.CallOption) (*RelayResponse, error) - Lookup(ctx context.Context, in *LookupRequest, opts ...grpc.CallOption) (*RelayResponse, error) - CloseName(ctx context.Context, in *LookupRequest, opts ...grpc.CallOption) (*CloseResponse, error) -} - -type relayClient struct { - cc grpc.ClientConnInterface -} - -func NewRelayClient(cc grpc.ClientConnInterface) RelayClient { - return &relayClient{cc} -} - -func (c *relayClient) RegisterName(ctx context.Context, in *RelayRequest, opts ...grpc.CallOption) (*RelayResponse, error) { - out := new(RelayResponse) - err := c.cc.Invoke(ctx, "/Relay/RegisterName", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *relayClient) Lookup(ctx context.Context, in *LookupRequest, opts ...grpc.CallOption) (*RelayResponse, error) { - out := new(RelayResponse) - err := c.cc.Invoke(ctx, "/Relay/Lookup", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *relayClient) CloseName(ctx context.Context, in *LookupRequest, opts ...grpc.CallOption) (*CloseResponse, error) { - out := new(CloseResponse) - err := c.cc.Invoke(ctx, "/Relay/CloseName", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// RelayServer is the server API for Relay service. -// All implementations must embed UnimplementedRelayServer -// for forward compatibility -type RelayServer interface { - RegisterName(context.Context, *RelayRequest) (*RelayResponse, error) - Lookup(context.Context, *LookupRequest) (*RelayResponse, error) - CloseName(context.Context, *LookupRequest) (*CloseResponse, error) - mustEmbedUnimplementedRelayServer() -} - -// UnimplementedRelayServer must be embedded to have forward compatible implementations. -type UnimplementedRelayServer struct { -} - -func (UnimplementedRelayServer) RegisterName(context.Context, *RelayRequest) (*RelayResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method RegisterName not implemented") -} -func (UnimplementedRelayServer) Lookup(context.Context, *LookupRequest) (*RelayResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method Lookup not implemented") -} -func (UnimplementedRelayServer) CloseName(context.Context, *LookupRequest) (*CloseResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method CloseName not implemented") -} -func (UnimplementedRelayServer) mustEmbedUnimplementedRelayServer() {} - -// UnsafeRelayServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to RelayServer will -// result in compilation errors. -type UnsafeRelayServer interface { - mustEmbedUnimplementedRelayServer() -} - -func RegisterRelayServer(s grpc.ServiceRegistrar, srv RelayServer) { - s.RegisterService(&Relay_ServiceDesc, srv) -} - -func _Relay_RegisterName_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(RelayRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(RelayServer).RegisterName(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/Relay/RegisterName", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(RelayServer).RegisterName(ctx, req.(*RelayRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Relay_Lookup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(LookupRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(RelayServer).Lookup(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/Relay/Lookup", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(RelayServer).Lookup(ctx, req.(*LookupRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Relay_CloseName_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(LookupRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(RelayServer).CloseName(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/Relay/CloseName", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(RelayServer).CloseName(ctx, req.(*LookupRequest)) - } - return interceptor(ctx, in, info, handler) -} - -// Relay_ServiceDesc is the grpc.ServiceDesc for Relay service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var Relay_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "Relay", - HandlerType: (*RelayServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "RegisterName", - Handler: _Relay_RegisterName_Handler, - }, - { - MethodName: "Lookup", - Handler: _Relay_Lookup_Handler, - }, - { - MethodName: "CloseName", - Handler: _Relay_CloseName_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "relay.proto", -} diff --git a/relay/relay.go b/relay/relay.go deleted file mode 100644 index abd7d93..0000000 --- a/relay/relay.go +++ /dev/null @@ -1,79 +0,0 @@ -package relay - -import ( - "context" - "fmt" - "sync" - - pb "github.com/boozec/rahanna/relay/proto" -) - -type Server struct { - pb.UnimplementedRelayServer -} - -type name string - -// TODO: use pair of ips and ports -type ips struct { - ip0 string - ip1 string -} - -var mu sync.Mutex - -// Map each name to a pair of IPs -var table = make(map[name]ips) - -func (s *Server) RegisterName(ctx context.Context, in *pb.RelayRequest) (*pb.RelayResponse, error) { - mu.Lock() - defer mu.Unlock() - - if in.Ip == "" { - return nil, fmt.Errorf("IP address cannot be empty") - } - - sessionName := NewSession() - for { - if _, ok := table[name(sessionName)]; !ok { - break - } - sessionName = NewSession() - } - - table[name(sessionName)] = ips{ip0: in.Ip, ip1: ""} - return &pb.RelayResponse{Name: sessionName, Ip: in.Ip}, nil -} - -func (s *Server) Lookup(ctx context.Context, in *pb.LookupRequest) (*pb.RelayResponse, error) { - mu.Lock() - defer mu.Unlock() - - if in.Name == "" { - return nil, fmt.Errorf("name cannot be empty") - } - - entry, ok := table[name(in.Name)] - if !ok { - return nil, fmt.Errorf("name not found") - } - - return &pb.RelayResponse{Name: in.Name, Ip: entry.ip0}, nil -} - -func (s *Server) CloseName(ctx context.Context, in *pb.LookupRequest) (*pb.CloseResponse, error) { - mu.Lock() - defer mu.Unlock() - - if in.Name == "" { - return nil, fmt.Errorf("name cannot be empty") - } - - _, ok := table[name(in.Name)] - if !ok { - return &pb.CloseResponse{Status: false}, fmt.Errorf("name not found") - } - - delete(table, name(in.Name)) - return &pb.CloseResponse{Status: true}, nil -} diff --git a/relay/relay_test.go b/relay/relay_test.go deleted file mode 100644 index 25fcc35..0000000 --- a/relay/relay_test.go +++ /dev/null @@ -1,86 +0,0 @@ -package relay - -import ( - "context" - "testing" - - pb "github.com/boozec/rahanna/relay/proto" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestRegisterName(t *testing.T) { - server := &Server{} - ctx := context.Background() - - t.Run("Valid IP Registration", func(t *testing.T) { - response, err := server.RegisterName(ctx, &pb.RelayRequest{Ip: "192.168.1.1"}) - require.NoError(t, err) - assert.NotEmpty(t, response.Name) - assert.Equal(t, "192.168.1.1", response.Ip) - }) - - t.Run("Empty IP Registration", func(t *testing.T) { - response, err := server.RegisterName(ctx, &pb.RelayRequest{Ip: ""}) - assert.Error(t, err) - assert.Nil(t, response) - }) - -} - -func TestLookup(t *testing.T) { - server := &Server{} - ctx := context.Background() - - registerResponse, err := server.RegisterName(ctx, &pb.RelayRequest{Ip: "192.168.1.1"}) - require.NoError(t, err) - - t.Run("Successful Lookup", func(t *testing.T) { - response, err := server.Lookup(ctx, &pb.LookupRequest{Name: registerResponse.Name}) - require.NoError(t, err) - assert.Equal(t, registerResponse.Name, response.Name) - assert.Equal(t, "192.168.1.1", response.Ip) - }) - - t.Run("Lookup with Empty Name", func(t *testing.T) { - response, err := server.Lookup(ctx, &pb.LookupRequest{Name: ""}) - assert.Error(t, err) - assert.Nil(t, response) - }) - - t.Run("Lookup Non-Existent Name", func(t *testing.T) { - response, err := server.Lookup(ctx, &pb.LookupRequest{Name: "nonexistent"}) - assert.Error(t, err) - assert.Nil(t, response) - }) -} - -func TestCloseName(t *testing.T) { - server := &Server{} - ctx := context.Background() - - // Prepare a registered name - registerResponse, err := server.RegisterName(ctx, &pb.RelayRequest{Ip: "192.168.1.1"}) - require.NoError(t, err) - - t.Run("Successful Close", func(t *testing.T) { - response, err := server.CloseName(ctx, &pb.LookupRequest{Name: registerResponse.Name}) - require.NoError(t, err) - assert.True(t, response.Status) - - // Verify the name is no longer in the table - _, err = server.Lookup(ctx, &pb.LookupRequest{Name: registerResponse.Name}) - assert.Error(t, err) - }) - - t.Run("Close with Empty Name", func(t *testing.T) { - _, err := server.CloseName(ctx, &pb.LookupRequest{Name: ""}) - assert.Error(t, err) - }) - - t.Run("Close Non-Existent Name", func(t *testing.T) { - response, err := server.CloseName(ctx, &pb.LookupRequest{Name: "nonexistent"}) - assert.Error(t, err) - assert.False(t, response.Status) - }) -} diff --git a/ui/views/play.go b/ui/views/play.go index d4f6b3b..389c302 100644 --- a/ui/views/play.go +++ b/ui/views/play.go @@ -32,8 +32,8 @@ var chess string = ` ` type playKeyMap struct { - EnterNewPlay key.Binding - StartNewPlay key.Binding + EnterNewGame key.Binding + StartNewGame key.Binding GoLogout key.Binding Quit key.Binding } @@ -43,12 +43,12 @@ type playResponse struct { Error string `json:"error"` } -var defaultPlayKeyMap = playKeyMap{ - EnterNewPlay: key.NewBinding( +var defaultGameKeyMap = playKeyMap{ + EnterNewGame: key.NewBinding( key.WithKeys("alt+E", "alt+e"), key.WithHelp("Alt+E", "Enter a play using code"), ), - StartNewPlay: key.NewBinding( + StartNewGame: key.NewBinding( key.WithKeys("alt+s", "alt+s"), key.WithHelp("Alt+S", "Start a new play"), ), @@ -67,7 +67,7 @@ type PlayModelPage int const ( LandingPage PlayModelPage = iota InsertCodePage - StartPlayPage + StartGamePage ) type PlayModel struct { @@ -79,7 +79,7 @@ type PlayModel struct { page PlayModelPage isLoading bool playName string - play *database.Play + play *database.Game } func NewPlayModel(width, height int) PlayModel { @@ -95,7 +95,7 @@ func NewPlayModel(width, height int) PlayModel { width: width, height: height, err: nil, - keys: defaultPlayKeyMap, + keys: defaultGameKeyMap, namePrompt: namePrompt, page: LandingPage, isLoading: false, @@ -122,14 +122,14 @@ func (m PlayModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case tea.KeyMsg: switch { - case key.Matches(msg, m.keys.EnterNewPlay): + case key.Matches(msg, m.keys.EnterNewGame): m.page = InsertCodePage return m, nil - case key.Matches(msg, m.keys.StartNewPlay): - m.page = StartPlayPage + case key.Matches(msg, m.keys.StartNewGame): + m.page = StartGamePage if !m.isLoading { m.isLoading = true - return m, m.newPlayCallback() + return m, m.newGameCallback() } case key.Matches(msg, m.keys.GoLogout): return m, m.logout() @@ -139,7 +139,7 @@ func (m PlayModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if m.page == InsertCodePage { if !m.isLoading { m.isLoading = true - return m, m.enterPlay() + return m, m.enterGame() } } } @@ -155,7 +155,7 @@ func (m PlayModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.playName = msg.Name } return m, nil - case database.Play: + case database.Game: m.isLoading = false m.play = &msg m.err = nil @@ -223,7 +223,7 @@ func (m PlayModel) View() string { ) } - case StartPlayPage: + case StartGamePage: var statusMsg string if m.isLoading { statusMsg = "Loading..." @@ -256,8 +256,8 @@ func (m PlayModel) View() string { ) } - enterKey := fmt.Sprintf("%s %s", altCodeStyle.Render(m.keys.EnterNewPlay.Help().Key), m.keys.EnterNewPlay.Help().Desc) - startKey := fmt.Sprintf("%s %s", altCodeStyle.Render(m.keys.StartNewPlay.Help().Key), m.keys.StartNewPlay.Help().Desc) + enterKey := fmt.Sprintf("%s %s", altCodeStyle.Render(m.keys.EnterNewGame.Help().Key), m.keys.EnterNewGame.Help().Desc) + startKey := fmt.Sprintf("%s %s", altCodeStyle.Render(m.keys.StartNewGame.Help().Key), m.keys.StartNewGame.Help().Desc) logoutKey := fmt.Sprintf("%s %s", altCodeStyle.Render(m.keys.GoLogout.Help().Key), m.keys.GoLogout.Help().Desc) quitKey := fmt.Sprintf("%s %s", altCodeStyle.Render(m.keys.Quit.Help().Key), m.keys.Quit.Help().Desc) @@ -286,7 +286,7 @@ func (m PlayModel) View() string { ) } -func (m PlayModel) newPlayCallback() tea.Cmd { +func (m PlayModel) newGameCallback() tea.Cmd { return func() tea.Msg { f, err := os.Open(".rahannarc") if err != nil { @@ -352,7 +352,7 @@ func (m PlayModel) newPlayCallback() tea.Cmd { } } -func (m PlayModel) enterPlay() tea.Cmd { +func (m PlayModel) enterGame() tea.Cmd { return func() tea.Msg { f, err := os.Open(".rahannarc") if err != nil { @@ -370,7 +370,7 @@ func (m PlayModel) enterPlay() tea.Cmd { fmt.Println("Error during scanning:", err) } - url := os.Getenv("API_BASE") + "/enter-play" + url := os.Getenv("API_BASE") + "/enter-game" port, err := network.GetRandomAvailablePort() if err != nil { @@ -409,7 +409,7 @@ func (m PlayModel) enterPlay() tea.Cmd { return playResponse{Error: response.Error} } - var response database.Play + var response database.Game err = json.NewDecoder(resp.Body).Decode(&response) if err != nil { return playResponse{Error: fmt.Sprintf("Error decoding JSON: %v", err)} |