Skip to content

Commit

Permalink
Merge pull request #180 from robinje/dynamodb
Browse files Browse the repository at this point in the history
Conversion from Bbolt DB to DynamoDB.
  • Loading branch information
robinje authored Aug 28, 2024
2 parents cb0e064 + d720c0e commit b51561c
Show file tree
Hide file tree
Showing 28 changed files with 1,079 additions and 1,051 deletions.
9 changes: 0 additions & 9 deletions .github/workflows/go-auto-format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,6 @@ jobs:
cd ../ssh_server
go fmt ./...
go mod tidy
cd ../database
go fmt ./...
go mod tidy
cd ../database_viewer
go fmt ./...
go mod tidy
cd ../database_create_item
go fmt ./...
go mod tidy
cd ..
git add . -v
if ! git diff-index --quiet HEAD; then
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,11 @@
# Ignore the config file.

**/config.json
**/config.yaml
**/config.yml

**/*.key
**/*.pem

# Ignore bolt files.

Expand Down
57 changes: 44 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,24 +59,55 @@ The current implementation includes an SSH server for secure authentication and

Deploying the server involves several steps, from setting up your environment to running the server. Follow these steps to ensure a smooth deployment process:

1. **Install Go**: The server is written in Go, so you need to have Go installed on your system. Download it from the [Go website](https://golang.org/).
1. **Install Python**: The server and utility scripts are now written in Python. Install Python 3.7 or later from the [Python website](https://www.python.org/).

2. **Set Up AWS Account**: An AWS account is required for deploying certain components of the server, such as the authentication system. Sign up for an account [here](https://aws.amazon.com/) if you don't already have one.
2. **Set Up AWS Account**: An AWS account is required for deploying the server components, including DynamoDB and Cognito. Sign up for an account [here](https://aws.amazon.com/) if you don't already have one.

3. **Configure AWS Credentials**: Ensure you have AWS credentials configured on your machine. These credentials should have sufficient permissions to create a Cognito user pool and the necessary IAM policies and roles. You can configure your credentials by using the AWS CLI and running `aws configure`.
3. **Configure AWS Credentials**: Ensure you have AWS credentials configured on your machine. These credentials should have sufficient permissions to create DynamoDB tables, a Cognito user pool, and the necessary IAM policies and roles. You can configure your credentials by using the AWS CLI and running `aws configure`.

4. **Deploy Cognito and IAM Resources**:
4. **Install Required Python Packages**: Install the necessary Python packages by running:
```
pip install boto3 pyyaml
```

5. **Deploy AWS Resources**:
- Navigate to the `scripts` directory within the project.
- Run the `deploy_cognito.py` script using the command `python deploy_cognito.py`. This script will create the Cognito instance along with the required IAM policies and roles. It will also generate the `config.json` file needed to run the server. Ensure you have Python installed on your machine to execute this script.

5. **Install Go Dependencies**: Before starting the server, you need to install the necessary Go dependencies. In the root directory of the project, run `go mod download` to fetch all required packages.

6. **Initalize the Database**: The server uses a BoltDB database for the world data. You can initialize the database by running the `data_loader.go` script located in the `database` directory. Run the script using the command `go run .`. The output will be the `test_data.bolt` file, which contains the initial world data. Copy this file to the `mud` directory.

7. **Start the Server**: Finally, you can start the server by running `go run .` from the root directory of the project. This command compiles and runs the Go application, starting up your MUD server.

Ensure all steps are completed without errors before trying to connect to the server. If you encounter any issues during deployment, refer to the specific tool's documentation for troubleshooting advice.
- Run the `deploy.py` script using the command:
```
python deploy.py
```
This script will create the Cognito user pool, DynamoDB tables, and CodeBuild project. It will also generate the `config.yml` file needed to run the server.
6. **Initialize the Database**:
- Navigate to the directory containing the `data_loader.py` script.
- Run the script using the command:
```
python data_loader.py -r rooms.json -a archetypes.json -p prototypes.json
```
This will load the initial world data into your DynamoDB tables.
7. **Start the Server**: Start the server by running the main Python script from the root directory of the project:
```
python main.py
```
8. **Verify Deployment**: You can verify the deployment by using the viewer script:
```
python viewer.py --region your-aws-region
```
This will display the contents of your DynamoDB tables.
Ensure all steps are completed without errors before trying to connect to the server. If you encounter any issues during deployment, refer to the AWS documentation or the specific tool's documentation for troubleshooting advice.
## Additional Tools
- **Create Item**: To add new items to rooms, use the `create_item.py` script:
```
python create_item.py
```
Follow the prompts to select a room and an item prototype, and the script will create a new item in the specified room.
Remember to keep your AWS credentials secure and never commit them to version control. It's recommended to use AWS IAM roles and policies to manage permissions securely.advice.
## License
Expand Down
164 changes: 164 additions & 0 deletions cloudformation/dynamo.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
AWSTemplateFormatVersion: '2010-09-09'
Description: 'DynamoDB tables for Multi-User Dungeon game'

Resources:
PlayersTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: players
AttributeDefinitions:
- AttributeName: PlayerName
AttributeType: S
KeySchema:
- AttributeName: PlayerName
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 2
WriteCapacityUnits: 2

CharactersTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: characters
AttributeDefinitions:
- AttributeName: CharacterID
AttributeType: S
KeySchema:
- AttributeName: CharacterID
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 2
WriteCapacityUnits: 2

RoomsTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: rooms
AttributeDefinitions:
- AttributeName: RoomID
AttributeType: N
KeySchema:
- AttributeName: RoomID
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 2
WriteCapacityUnits: 2

ExitsTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: exits
AttributeDefinitions:
- AttributeName: RoomID
AttributeType: N
- AttributeName: Direction
AttributeType: S
KeySchema:
- AttributeName: RoomID
KeyType: HASH
- AttributeName: Direction
KeyType: RANGE
ProvisionedThroughput:
ReadCapacityUnits: 2
WriteCapacityUnits: 2

ItemsTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: items
AttributeDefinitions:
- AttributeName: ItemID
AttributeType: S
KeySchema:
- AttributeName: ItemID
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 2
WriteCapacityUnits: 2

ItemPrototypesTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: prototypes
AttributeDefinitions:
- AttributeName: PrototypeID
AttributeType: S
KeySchema:
- AttributeName: PrototypeID
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 2
WriteCapacityUnits: 2

ArchetypesTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: archetypes
AttributeDefinitions:
- AttributeName: ArchetypeName
AttributeType: S
KeySchema:
- AttributeName: ArchetypeName
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 2
WriteCapacityUnits: 2

MUDDynamoDBPolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
ManagedPolicyName: MUDDynamoDBReadWritePolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
- dynamodb:BatchGetItem
- dynamodb:BatchWriteItem
- dynamodb:Query
- dynamodb:Scan
Resource:
- !Sub 'arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/players'
- !Sub 'arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/characters'
- !Sub 'arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/rooms'
- !Sub 'arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/exits'
- !Sub 'arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/items'
- !Sub 'arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/prototypes'
- !Sub 'arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/archetypes'

Outputs:
PlayersTableArn:
Description: "ARN of the Players table"
Value: !GetAtt PlayersTable.Arn

CharactersTableArn:
Description: "ARN of the Characters table"
Value: !GetAtt CharactersTable.Arn

RoomsTableArn:
Description: "ARN of the Rooms table"
Value: !GetAtt RoomsTable.Arn

ExitsTableArn:
Description: "ARN of the Exits table"
Value: !GetAtt ExitsTable.Arn

ItemsTableArn:
Description: "ARN of the Items table"
Value: !GetAtt ItemsTable.Arn

ItemPrototypesTableArn:
Description: "ARN of the ItemPrototypes table"
Value: !GetAtt ItemPrototypesTable.Arn

ArchetypesTableArn:
Description: "ARN of the Archetypes table"
Value: !GetAtt ArchetypesTable.Arn

MUDDynamoDBPolicyArn:
Description: "ARN of the MUD DynamoDB Read/Write Policy"
Value: !Ref MUDDynamoDBPolicy
87 changes: 37 additions & 50 deletions core/archtype.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import (
"log"
"os"

bolt "go.etcd.io/bbolt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/dynamodb"
"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
)

func DisplayArchetypes(archetypes *ArchetypesData) {
Expand All @@ -15,75 +17,60 @@ func DisplayArchetypes(archetypes *ArchetypesData) {
}
}

func LoadArchetypesFromJSON(fileName string) (*ArchetypesData, error) {
file, err := os.ReadFile(fileName)
if err != nil {
return nil, err
}
func (kp *KeyPair) LoadArchetypes() (*ArchetypesData, error) {
archetypesData := &ArchetypesData{Archetypes: make(map[string]Archetype)}

var data ArchetypesData
err = json.Unmarshal(file, &data)
var archetypes []Archetype
err := kp.Scan("archetypes", &archetypes)
if err != nil {
return nil, err
return nil, fmt.Errorf("error scanning archetypes table: %w", err)
}

for key, archetype := range data.Archetypes {
fmt.Printf("Loaded archetype '%s': %s - %s\n", key, archetype.Name, archetype.Description)
for _, archetype := range archetypes {
archetypesData.Archetypes[archetype.Name] = archetype
fmt.Printf("Loaded archetype '%s': %s\n", archetype.Name, archetype.Description)
}

return &data, nil
return archetypesData, nil
}

func (kp *KeyPair) StoreArchetypes(archetypes *ArchetypesData) error {
kp.Mutex.Lock()
defer kp.Mutex.Unlock()

return kp.db.Update(func(tx *bolt.Tx) error {
bucket, err := tx.CreateBucketIfNotExists([]byte("Archetypes"))
for _, archetype := range archetypes.Archetypes {
av, err := dynamodbattribute.MarshalMap(archetype)
if err != nil {
return fmt.Errorf("create archetypes bucket: %w", err)
return fmt.Errorf("error marshalling archetype %s: %w", archetype.Name, err)
}

for key, archetype := range archetypes.Archetypes {
data, err := json.Marshal(archetype)
if err != nil {
return fmt.Errorf("marshal archetype %s: %w", key, err)
}
if err := bucket.Put([]byte(key), data); err != nil {
return fmt.Errorf("store archetype %s: %w", key, err)
}
log.Printf("Stored archetype: %s", key)
key := map[string]*dynamodb.AttributeValue{
"Name": {S: aws.String(archetype.Name)},
}
return nil
})
}

func (kp *KeyPair) LoadArchetypes() (*ArchetypesData, error) {
kp.Mutex.Lock()
defer kp.Mutex.Unlock()
err = kp.Put("archetypes", key, av)
if err != nil {
return fmt.Errorf("error storing archetype %s: %w", archetype.Name, err)
}

archetypesData := &ArchetypesData{Archetypes: make(map[string]Archetype)}
log.Printf("Stored archetype: %s", archetype.Name)
}

err := kp.db.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte("Archetypes"))
if bucket == nil {
return fmt.Errorf("archetypes bucket does not exist")
}
return nil
}

return bucket.ForEach(func(k, v []byte) error {
var archetype Archetype
if err := json.Unmarshal(v, &archetype); err != nil {
return err
}
fmt.Println("Reading", string(k), archetype)
archetypesData.Archetypes[string(k)] = archetype
return nil
})
})
func LoadArchetypesFromJSON(fileName string) (*ArchetypesData, error) {
file, err := os.ReadFile(fileName)
if err != nil {
return nil, err
}

var data ArchetypesData
err = json.Unmarshal(file, &data)
if err != nil {
return nil, err
}

return archetypesData, nil
for key, archetype := range data.Archetypes {
fmt.Printf("Loaded archetype '%s': %s - %s\n", key, archetype.Name, archetype.Description)
}

return &data, nil
}
Loading

0 comments on commit b51561c

Please sign in to comment.