301 Days

A year of gamedev experiments.

Day 55 - Two More

| Comments

An easy one

features/connect.featurelink
12
13
14
15
16
Scenario: Allow connection if server in Running mode
Given I use the "minimal" database as-is
When the server executable is started
And I allow time for the server to complete startup
Then I can connect to the server via telnet

This one’s pretty trivial; we just implement the opposite steps to the ones we did for “Disallow connection if server in Starting mode”. In truth, it’s somewhat redundant; if this didn’t work, none of the login tests would work, but we’ll leave it here for completeness.

1
2
3
7 scenarios (7 passed)
33 steps (33 passed)
2m14.275s

useradd

features/connect.featurelink
19
20
21
22
23
24
25
26
27
Scenario: Allow connection if server in Locked mode
Given I use the "minimal" database as-is
When the server executable is started
And I allow time for the server to complete startup
When I log on using a developer account
And I enter the chat
And I issue the "/lockserver" command
And I disconnect from the server
Then I can connect to the server via telnet

To get the server in Locked mode, we need to have an admin log in and lock it. Let’s see what allows a user to use /lockserver

DragonsSpine/Menus/Conference.cslink
1669
1670
1671
1672
1673
1674
1675
1676
case "/lockserver":
    if (ch.ImpLevel >= Globals.eImpLevel.DEV)
    {
        DragonsSpineMain.ServerStatus = DragonsSpineMain.ServerState.Locked;
        ch.WriteLine("Game world has been locked.", Protocol.TextType.System);
    }
    else { SendInvalidCommand(ch); }
    break;

impLevel is in the Player table, so we need to make sure we have an Account and a Player. We start with an ugly-but-it-works solution, but then split it up into reasonable methods:

features/steps/login_steps.rblink
164
165
166
167
168
169
170
171
When(/^I log on using a developer account$/) do
  @user = { acct_name: "dev01", acct_password: "devpass01" }
  ensure_developer_account_exists(@user[:acct_name], @user[:acct_password])
  @connection = create_telnet_connection
  telnet_command(@connection, "", /Login\: /, {"Timeout" => 120})
  telnet_commands(@connection, [[@user[:acct_name], /Password\: /],
                                [@user[:acct_password], /Command\: /]])
end
features/lib/db_helper.rblink
65
66
67
68
69
70
71
72
73
74
75
76
77
78
def ensure_developer_account_exists(acct_name, acct_password)
  accountID = find_or_create_account(acct_name, acct_password)
  # now that we have an accountID we can make the player
  client = connect_to_db(@server_database)
  # first delete any players already attached to this accountID
  query = "DELETE \
    FROM [#{@server_database}].[dbo].[Player]
    WHERE [accountID] = #{accountID}"
  result = client.execute(query)
  affected_rows = result.do
  puts "Pre-existing players for account ID #{accountID} deleted: #{affected_rows}"
  # now insert the new player with DEV access
  db_insert_player({accountID: accountID, account: acct_name, impLevel: 4})
end
features/lib/db_helper.rblink
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
def find_or_create_account(acct_name, acct_password)
  client = connect_to_db(@server_database)
  # Nothing prevents identical users, so we need to check if it's already there:
  accountID = nil
  result = client.execute("SELECT * FROM [#{@server_database}].[dbo].[Account] \
                          WHERE [account] = '#{acct_name}'")
  result.each do |row|
    accountID = row['accountID']
  end
  fail "duplicate users!" if result.affected_rows > 1
  if nil == accountID
    query =  "INSERT INTO [#{@server_database}].[dbo].[Account] \
      ([account],[password],[email]) VALUES \
      ('#{acct_name}', '#{acct_password}', '#{acct_name}@test.not')"
    puts query
    result = client.execute(query)
    affected_rows = result.do
    puts affected_rows
    fail "Failed to insert user!" if 1 != affected_rows
  end
  result = client.execute("SELECT * FROM [#{@server_database}].[dbo].[Account] \
                          WHERE [account] = '#{acct_name}'")
  result.each do |row|
    puts row
    accountID = row['accountID']
  end
  fail "duplicate users!" if result.affected_rows > 1
  accountID
end
features/lib/db/player.rblink
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# Plan to build this out over time as necessary
DEFAULT_PLAYER = {
    accountID: 0,
    account: 'no_name',
    name: 'no_name',
    impLevel: 0
}

def db_insert_player(db_hash)
  # anything in the hash overrides defaults
  player = DEFAULT_PLAYER.merge(db_hash)
  client = connect_to_db(@server_database)
  query = "INSERT INTO [#{@server_database}].[dbo].[Player] ([notes], [accountID], [account], \
    [name], [gender], [race], [classFullName], [classType], [visualKey], [alignment], [confRoom], \
    [impLevel], \
    [ancestor], [ancestorID], [facet], [land], [map], [xCord], [yCord], [zCord], [dirPointer], \
    [stunned], [floating], [dead], [fighterSpecialization], [level], [exp], [hits], [hitsMax], \
    [hitsAdjustment], [hitsDoctored], [stamina], [stamLeft], [staminaAdjustment], [mana], \
    [manaMax], [manaAdjustment], [age], [roundsPlayed], [numKills], [numDeaths], [bankGold], \
    [strength], [dexterity], [intelligence], [wisdom], [constitution], [charisma], [strengthAdd], \
    [dexterityAdd], [birthday], [lastOnline], [deleteDate], [currentKarma], [lifetimeKarma], \
    [lifetimeMarks], [pvpKills], [pvpDeaths], [playersKilled], [playersFlagged], [UW_hitsMax], \
    [UW_hitsAdjustment], [UW_staminaMax], [UW_staminaAdjustment], [UW_manaMax], \
    [UW_manaAdjustment], [UW_intestines], [UW_liver], [UW_lungs], [UW_stomach]) \
    VALUES (N'', #{player[:accountID]}, \
    N'#{player[:account]}', \
    N'#{player[:name]}', \
    1, N'Illyria', N'Fighter', \
    N'Fighter', N'male_fighter_pc_brown', 1, 0, #{player[:impLevel]}, 0, 0, 0, 0, 0, 42, 27, 0, \
    N'v', 0, 3, 0, N'None', 4, 4996, 36, 36, 0, 0, 10, 10, 0, 0, 0, 0, 806, 806, 8, \
    0, 0, 17, 18, 10, 17, 8, 18, 1, 1, CAST(0x0000A4FC0001D163 AS DateTime), \
    CAST(0x0000A4FC001A5F6A AS DateTime), CAST(0x0000979200000000 AS DateTime), \
    0, 0, 0, 0, 0, N'', N'', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)"
  result = client.execute(query)
  affected_rows = result.do
  fail "Unable to insert player!" if 1 != affected_rows
end

After this, the steps for getting into the chat and issuing the lock command are pretty trivial.

1
2
3
8 scenarios (8 passed)
42 steps (42 passed)
2m5.928s

Slow going

Making such slow progress, it seems like I’ll never get a decent set of tests up and running. But we’re building up an inventory of reusable code that will make future tests easier to implement.

The big question is: How do we approach such a big test project, really, and how do we organize the work? I’ve been considering that, and think I have an approach to try. Tomorrow.


Day 55 code - tests

Comments