YO-TAROU


Project Cars 2 – Dedicated Serverの建て方はクセがある

10/17 アプデによって以下のようなバグは解消されていました

細かくチェックしてる訳ではないので、少なくとも深刻なバグはありませんでした。現在の設定コードをこちらに掲載しました。

10/3 報告されているバグがまとめられています。以下引用

深刻なバグ

  • controlGameSetup : true,にしていると1名(ホスト)入室すると他者は参加できなくなる。
  • controlGameSetup : false,にすると他者は参加できるが、ホストと同じ車種しか選択できなくなる。だけど私のcfgを見てください。解決しています。

迷惑なバグ

  • ロビーのサーバー設定表示画面にcfgで設定した項目が正しく反映されていない。以下3つ。
    • セッションの日付が必ずTodayになる。実際はcfgの日付が反映されている。
    • 接触の有無が無しになるが実際はcfgの設定が反映されている。
    • マニュアルピットコントロールが無しになるが実際はcfgの設定が反映されている。
  • サーバーの初回起動時に必ず14名の熱心なAIが参加する。一度退出し再度参加するといなくなる。

ちなみにこれらはローテーションをオフの環境です。

10/1 hostportがおかしいかもしれない人は以下参照

server.cfgのhostportを9000へ変更、httpapiportを27017へ変更したらなんとか人は入れてるようだ。他人が入れないのはhostportに関係してるっぽい動き。(うちの環境だけかもしれないけど)

先日発売されたpCars2ですが、専用サーバーを立てるには少しクセがあります。色々分かってきたので書き記したいと思います。サーバー用ファイルはSteamのToolよりダウンロードします。ファイル構成は以下の通り。

Dedicated Serverのファイル構成

files
サーバーの起動は DedicatedServerCmd.exe で行いますが、先にサーバー内容を編集します。基本設定は server.cfg をテキストエディタで行います。また、セッションをオートローテーションする機能もあり、その内容を編集する場合は次のファイルを編集します。luaフォルダを開き・・
files2
sms_rotateを開き・・
files3
sms_rotate_default_config.json がローテーション構成を設定し、尚且つ server.cfg 内の基本設定を上書きしてサーバー設定が決まります。

*****************************************************************

server.cfgで基本設定

以下のserver.cfgはデフォルトから手を加えています。主な変更はhttpapiの設定(後述)など。

"//" : "You can use dummy entries like this to write comments into the config. 'rem' and '#' are also supported as comment entries."
// But in recent version of the server, standard C++ like one-liner comments are supported as well.

//////////////////////////
// Basic server options //
//////////////////////////

// Logging level of the server. Messages of this severity and more important will be logged. Can be any of debug/info/warning/error.
logLevel : "info"

// Number of gameplay events stored on the server. Oldest ones will be discarded once the game logs more.
eventsLogSize : 10000

// The server's name, this will appear in server browser (when implemented) and will be also the default name of sessions hosted on the server.
name : "Your Server Name"

// Authenticate users with Steam to check VAC ban when set to true.
secure : true

// Password required to create sessions on the server as well as to join the sessions, password set in Create options is ignored on DS.
password : ""

// Maximum size of sessions that can be created on this server.
// Note that setting this above 16 will allow sessions unjoinable by 32-bit clients to be created on the server.
// The game also currently does not support sessions larger than 32, even if the server can be configured for up to 64 players.
maxPlayerCount : 24

// IP address where the server's sockets should be bound. Leave empty for 'all interfaces'.
bindIP : ""

// ports used to communicate with Steam and game, they must all be accessible on the public IP of the server.
steamPort : 8766
hostPort : 27015
queryPort : 27016

// Delay between server ticks in milliseconds, when not hosting and when hosting a game, respectively.
// Lower values will make the server more responsible and decrease latency by a bit at the expense of higher CPU usage.
sleepWaiting : 50
sleepActive : 10

// Sports Play will use system sockets instead of Steam networking API. Recommended for offline LAN-only events.
// Use cmdline switch -sportsplay serverIp:hostPort on client to host and join games on sportsPlay server.
sportsPlay: false

// (blacklist and whitelist is intentionally not included in this basic sample config as they can complicate things a bit.)
// whiteList : { "SteamName" : SteamUserID }

///////////////////////////
// HttpApi configuration //
///////////////////////////

// This provides http-based API and basic web-based controls using a built-in http server.
// HttpApi is disabled by default, and if you enable it with everything else left to default it will listen only locally on 127.0.0.1:9000

// Master enable/disable toggle.
enableHttpApi : true

// Similar to logLevel above but used only for libwebsockets output.
// Note that all logging still goes through the main filter, so you won't be able to use more verbose logging here than the main level.
httpApiLogLevel : "warning"

// Interface name or IP where to bind the local http server providing the API and web-based controls.
// This is the textual name of the interface as assigned by your OS, or IP address of the interface.
// The default value is "127.0.0.1", change it to an empty string to bind the listen socket to all available interfaces.
httpApiInterface : ""

// Port where the local http server listens.
httpApiPort : 9000

// Map with extra HTTP headers to add to HTTP API responses.
// The keys are the conditions for adding the headers, the values are the headers to add.
// There are no conditions supported yet, so just use "*" for a generic wildard for now, that will match everything in the future too.
httpApiExtraHeaders : {
    "*" : "Access-Control-Allow-Origin: *"
}

// Http API access level overrides.
// Each HTTP API endpoint defines its default access level, usually one of "public", "private" or "admin".
// This map can override these levels to anything else. The keys are wildcard endpoint paths, and the values are the
// access levels to use. The first path that matches will be used, processing them in the order as written here.
// The wildcard patterns are case-sensitive and can contain:
// - '*': matches 0 or more characters
// - '%': matches 0 or more characters except for forward slash
// - '?': matches 1 character
httpApiAccessLevels : {
    // The default is empty, using defaults as defined by the endpoints themselves.

    // But you could for example use this to change all access levels to public (not recommended!)
    // "*" : "public"

    // Or this to hide the status from public
    // "" : "private"
    // "status" : "private"

    // And similar to hide the help and lists from public
    // "api/help" : "private"
    // "api/list*" : "private"

    // As you can see from the example above, the paths should be written with no initial or trailing slashes.
}

// Filtering rules for the access levels.
// The default access levels are "public", "private" and "admin", but the httpAccessLevels above can define any additional levels.
// This map then tells the server who has access to which level. It's a map from level names to filtering rules.
//
// Filtering rules are then specified as a list of structures, processed in the order as written in the config.
// Each rule structure contains a type" and then type-specific fields. The supported types are:
// - "accept": Accept this request, no additional checks.
// - "reject": Reject this request, no additional checks.
// - "reject-password": Reject this request and let the client know that a password is required, no additional checks.
// - "ip-accept": Accept this request if it matches the "ip" mask in CIDR notation (for example, "192.168.1.0/24")
// - "ip-reject": Reject this request if it matches the "ip" mask in CIDR notation (for example, "192.168.1.0/24")
// - "user": Accept this request if it authenticates as given "user".
// - "group" : Accept this request if it authenticates as given "group".
//
// User/group authentication is done using the standard HTTP basic access authentication (https://en.wikipedia.org/wiki/Basic_access_authentication).
httpApiAccessFilters : {

    // Public rules. The default is to accept everything.
    "public" : [
        { "type" : "accept" }
    ],

    // Private rules. The default is to accept queries from localhost, queries authenticated as users in the "private" group
    // and to reject anything else.
    "private" : [
        { "type" : "ip-accept", "ip" : "127.0.0.1/32" },
        { "type" : "group", "group" : "private" },
        { "type" : "reject-password" }
    ],

    // Admin rules. The default is to accept queries from localhost, queries authenticated as users in the "admin" group
    // and to reject anything else.
    "admin" : [
        { "type" : "ip-accept", "ip" : "127.0.0.1/32" },
        { "type" : "group", "group" : "admin" },
        { "type" : "reject-password" }
    ],

}

// User list. Map from user names to passwords, in plain text.
httpApiUsers : {
    "USERNAMEHERE" : "PASSWORDHERE",
    // "michal" : "bar",
}

// User groups. Map from group names to lists of users in said groups.
httpApiGroups : {
     "private" : [ "USERNAMEHERE" ],
     "admin" : [ "USERNAMEHERE" ],
}

// Root directory where the static files for the web tool are located. Relative to current directory.
staticWebFiles: "web_files"


//////////////////////////
// LuaApi configuration //
//////////////////////////

// Lua API allows the server to be extended by in-server scripting in Lua.
// The server is running Lua version 5.3, currently with no sandboxing applied to the add-ons. All standard Lua library functions are available.

// WARNING: The LuaApi is not final and the following breaking changes are planned soon:
// - Rename Lua addon metadata files from *.txt to *.json
// - Remove default config from the metadata and put it into separate file *_default_config.json
// - Separate config and persistent data, so that data saved by addons will not pollute the config files, and the config files will retain all comments from the default files.

// Master enable/disable toggle.
enableLuaApi : true

// Root directory from which the Lua addons are loaded. Relative to current directory if it's not absolute.
luaAddonRoot: "lua"

// Root directory where the addon configs will be stored if written out by addons. Default configs are defined in the addon base text files.
luaConfigRoot: "lua_config"

// Root directory where the addon output will be written, once supported. For now the io functions can write anywhere, but this will be limited to this directory in the future.
luaOutputRoot: "lua_output"

// Names of all Lua addons to load. The addons will be loaded in the specified order. Each addon can list other addons as its dependencies, which attempt to load those first.
// The server will load addons from directory specified in "luaAddonRoot", loading of each addon will start by loading its config from ADDON_NAME/ADDON_NAME.txt
luaApiAddons : [

    // Core server bootup scripts and helper functions. This will be always loaded first even if not specified here because it's an implicit dependency of all addons.
    "sms_base",

    // Automatic race setup rotation.
    "sms_rotate",

    // Sends greetings messages to joining members, optionally with race setup info, optionally also whenever returning back to lobby post-race.
    "sms_motd",

    // Tracks various stats on the server - server, session and player stats.
    "sms_stats",
]

// Names of all lua libraries that are allowed to be used by any addons.
luaAllowedLibraries : [

    "lib_rotate"
]

////////////////////////////////
// Game setup control options //
////////////////////////////////

// Set to true to make this server show up in the browser even if it's empty.
allowEmptyJoin : true

// Set to true to enable API that allows the server to control the game's setup. The host will not be able to control the setup if this is set.
// Set to true to enable API that allows the server to control the game's setup. The host will not be able to control the setup if this is set.
// This must be set to "true" for the following attributes to work: ServerControlsTrack, ServerControlsVehicleClass, ServerControlsVehicle
controlGameSetup : true

// Initial attribute values, see /api/list/attributes/session for the full list.
// These attributes will be used when joining an empty server via the borwser (if allowEmptyJoin is true) and as the intial attributes for the set_attributes and set_next_attributes APIs (if controlGameSetup is true)
// The defaults set these values:
sessionAttributes : {
    // The host player can control track selection if set to 0. Set to 1 to disable track selection in the game.
    "ServerControlsTrack" : 1,

    // The host player can change the vehicle class by going through the garage if set to 0. Set to 1 to disallow players changing the class.
    // Flag FORCE_SAME_VEHICLE_CLASS (1024) should be also set for this to make sense, otherwise players are able to choose cars from any class.
    "ServerControlsVehicleClass" : 1,

    // Players can change their vehicle if set to 0. Set to 1 to disallow players changing the vehicle.
    // Flag FORCE_IDENTICAL_VEHICLES (2) should be also set for this to make sense.
    "ServerControlsVehicle" : 0,

    // Grid size up to 32, all reserved to players, so no AI.
    // Note that 32-bit clients will not be able to join the game if this is larger than 16.
    "GridSize" : 24,
    "MaxPlayers" : 24,

    // Just 3 race laps.
    "PracticeLength" : 20,
    "QualifyLength" : 15,
    "RaceLength" : 10,

    // Race flags - bitfield consisting of many flags, the default is set like this:
    // ALLOW_CUSTOM_VEHICLE_SETUP (8) + ABS_ALLOWED (32) + SC_ALLOWED (64) + TCS_ALLOWED (128) + "
    // FORCE_SAME_VEHICLE_CLASS (512) + FILL_SESSION_WITH_AI (131072) + AUTO_START_ENGINE (524288) = 656106"
    // See /api/list/flags/session in HTTP API, or globals "lists.flags.session" or "SessionFlags" in Lua API for the full list.
    "Flags" : -394001478,

    // AI opponent difficulty, from 0 to 100. Applies only on loading if GridSize is larger than MaxPlayers, and the FILL_SESSION_WITH_AI flag is enabled.
    "OpponentDifficulty" : 0,
 
    // Off 0, Visual Only 1, PerfomanceInpacting 2, Full 3
    "DamageType" : 3,
 
    // X7 0, X6 1, X5 2, X4 3, X3 4, X2 5, Standard 6, Slow 7, Off 8.
    "TireWearType" : 6,
 
    // Standard 0, Slow 1, Off 2.
    "FuelUsageType" : 0,
    "AllowedViews" : 0,
	
    // Penalties on.
    "PenaltiesType" : 1,
    "PitWhiteLinePenalty" : 1,
    "DriveThroughPenalty" : 1,
    "AllowablePenaltyTime" : 30,
    "PitControl" : 1,
    "ManualRollingStarts" : 1,
    "RaceRollingStart" : 1,
    "RaceFormationLap" : 1,
 
    // Any 0, CockpitHelmet Only 2.
    "AllowedViews" : 0,

    // Track Brands Hatch Indy. See /api/list/tracks/ in HTTP API or globals "lists.tracks", "id_to_track" or "name_to_track" in Lua API.
    "TrackId" : 545979690,

    // Vehicle class Road A. See /api/list/vehicle_classes/ in HTTP API or globals "lists.vehicle_classes", "id_to_vehicle_class" or "name_to_vehicle_class" in Lua API.
    "VehicleClassId" : -1529501352,

    // Vehicle McLaren 720S (but not forced by default flags). See /api/list/vehicles/ in HTTP API or globals "lists.vehicles", "id_to_vehicle" or "name_to_vehicle" in Lua API.
    "VehicleModelId" : -648709823,

    // Starting date and time.
    "RaceDateYear" : 2017,
    "RaceDateMonth" : 9,
    "RaceDateDay" : 25,
    "RaceDateHour" : 11,

 
    // Clear -934211870, LightCloud 296956818, MediumCloud 888299130, HeavyCloud 129238383,  Overcast -1293634875, LightRain 270338437, Rain 1461703858, FogWithRain -358600329, Foggy 2067843977, HeavyFog -754279862, Hazy -1299791789, HeavyFogWithRain -1604560069, Storm -1592958063, ThunderStorm -2112363295, Blizzard 282734801, snow -2057276008, heavysnow 1838012094, Random 1275961519.
    "RaceWeatherSlots" : 4,
    "RaceWeatherSlot1" : -934211870,
    "RaceWeatherSlot2" : -934211870,
    "RaceWeatherSlot3" : -934211870,
    "RaceWeatherSlot4" : -934211870,
}

5行目からのBasic server optionではサーバー名などの基本設定。
49行目からのHttpApi configurationではウェブ上からの設定を行う機能について。(後述)
159行目からのLuaApi configurationではローテーションやウェルカムメッセージなどの機能について。
206行目からのGame setup control optionsではセッションの基本設定。

Flagの設定について

Flagと言われても旗と思いがちですが、このゲームではルールを設定する項目となります。246行目にあるFlagsの項目には数字が書いてありますが、フォルダの中にあるServerTypes.pdfにルールと数字が次の画像のように一覧で書いています。
flags
簡単に言うと、サーバーでABSとTCSとSCを使用可能にしたいのであればABS_ALLOWED 32, SC_ALLOWED 64, TCS_ALLOWED 128の数字部分を足し算し246行目のFlagsの部分に224と書けばよいのです。(全体的に桁数が多いので間違わないように)その他、車種やトラック、ダメージの有無なども数字で表現されているので先ほどのPDFで確認できます。
ここではFlag値計算を自動で出してくれる。便利なのを発見!

ローテーションを使用する場合のファイルsms_rotate_default_config.json

※初回起動後はlua_config > sms_rotate_config.jsonが出来るので以後はこれを編集すればOK。
デフォルトには書いてなかった有効な項目を追加しています。参考までに掲載。大きくdefaultとrotationに分かれており、defaultには基本の設定を。rotationにはセッション毎の個別設定を行います。下記のrotationにはコースしか書いてませんが個別に項目を追加して、クラスを変えたり天気を変えたりラップ数を変えたりする事が出来ます。読み込む順位のイメージとしてはserver.cfgの設定をdefaultで上書きしrotationがさらに上書きするという感じです。rotationは上から順に進んでいきますが、”persist_index” : true,の場合0人になってもローテーションは記憶され続けます。最初のコースへ毎回戻したい場合はfalseにします。

// Config version.
version : 7

// Default configuration.
config : {

	// Is the current rotation index persistent? If true, the rotation will continue after server restart,
	// If false, the rotation will always start from the first setup.
	// You can always delete the sms_rotate_data.json file from lua_config to reset the persisted index.
	"persist_index" : true,

	// The default setup. This is a table with attributes and values. The following rules apply to the attributes:
	// - If TrackId is specified: The track to enforce. Automatically sets 1 to ServerControlsTrack
	// - If VehicleModelId is specified: The vehicle to enforce. Automatically sets 1 to ServerControlsVehicle, sets FORCE_IDENTICAL_VEHICLES to Flags
	// - If VehicleClassId is specified: The class to enforce. Automatically sets 1 to ServerControlsVehicleClass, sets FORCE_SAME_VEHICLE_CLASS to Flags
	// - Track, vehicle model, vehicle class and all enum/flags attributes can use values in string forms - so you can use either track id, or track name.
	// - Flags in string form can contain multiple flags separated by comma, so for example "ABS_ALLOWED,SC_ALLOWED,TCS_ALLOWED"
	// - The setup should never contain any of the "ServerControls" attributes, those are decided automatically.
	// - The setup should never contain both VehicleModelId and VehicleClassId at the same time. Restrict either the class or specific vehicle.
	// - The Flags attribute should never contain FORCE_IDENTICAL_VEHICLES or FORCE_SAME_VEHICLE_CLASS flags, those are decided automatically.
	"default" : {
		"GridSize" : 24,
		"MaxPlayers" : 24,
		"VehicleClassId" : "TC1",
		
		"PracticeLength" : 20,
		"QualifyLength" : 15,
		"RaceLength" : 10,

		"Flags" : -394001478,
		"DamageType" : "FULL",
		"TireWearType" : "STANDARD",
		"FuelUsageType" : "STANDARD",
		"PenaltiesType" : "FULL",
		"AllowedViews" : "Any",
		
		"PenaltiesType" : 1,
		"PitWhiteLinePenalty" : 1,
		"DriveThroughPenalty" : 1,
		"AllowablePenaltyTime" : 30,
		"ManualRollingStarts" : 1,
		"RaceRollingStart" : 1,
		"RaceFormationLap" : 1,
		
		// "MinimumOnlineRank" : "U",
		// "MinimumOnlineStrength" : 1200,
		
		"PracticeDateYear" : 2017,
		"PracticeDateMonth" : 9,
		"PracticeDateDay" : 25,
		"PracticeDateHour" : 10,
		
		"QualifyDateYear" : 2017,
		"QualifyDateMonth" : 9,
		"QualifyDateDay" : 25,
		"QualifyDateHour" : 13,

		"RaceDateYear" : 2017,
		"RaceDateMonth" : 9,
		"RaceDateDay" : 25,
		"RaceDateHour" : 15,

		"PracticeWeatherSlots" : 2,
		"PracticeWeatherSlot1" : "Clear",
		"PracticeWeatherSlot2" : "LightCloud",
		"PracticeWeatherSlot3" : "Clear",
		"PracticeWeatherSlot4" : "Clear",

		"QualifyWeatherSlots" : 2,
		"QualifyWeatherSlot1" : "LightCloud",
		"QualifyWeatherSlot2" : "Clear",
		"QualifyWeatherSlot3" : "Clear",
		"QualifyWeatherSlot4" : "Clear",

		"RaceWeatherSlots" : 4,
		"RaceWeatherSlot1" : "MediumCloud",
		"RaceWeatherSlot2" : "MediumCloud",
		"RaceWeatherSlot3" : "LightCloud",
		"RaceWeatherSlot4" : "Clear",
	},

	// The rotation. Array of setups to rotate. If empty, just the default setup will be used with no actual rotation happening.
	//
	// These setups are applied on top of the default setup, then applied to the game. Previous setup in the rotation is never used.
	// So for example if you wanted to repeat the same track in multiple consecutive setups, different from the default track,
	// each of those setups needs to explicitly include that track. Also remember that apart from the flags nothing can be "removed"
	// from the default setup, so if the default setup contains a track, some track will always be enforced (either the default one,
	// or the setup-specific one). And one last thing, these override setups can never include an attribute that's not specified in
	// the default setup - it can just override some of the default attributes, not add new ones. The only exception are the
	// track/vehicle/class attributes.
	//
	// Attributes in these setups can have a special attribute RemoveFlags set, which will remove the specified flags from the default
	// setup instead of adding them to it (which is what the Flags attribute will do)
	"rotation" : [
		{
			"TrackId" : "Oulton Park International",
		},
		{
			"TrackId" : "Mazda Raceway Laguna Seca",
		},
		{
			"TrackId" : "Bathurst Mount Panorama",
		},
		{
			"TrackId" : "Road America",
		},
		{
			"TrackId" : "Snetterton 300",
		},
		{
			"TrackId" : "Donington Park GP",
		},
		{
			"TrackId" : "Circuit de Barcelona-Catalunya GP",
		},
		{
			"TrackId" : "Brands Hatch GP",
		}
	]
}

httpapiを有効にしてウェブ上から監視&設定変更

server.cfg内の56行目をtrueにすることで有効にし65行目を空欄に変更します。144行目でユーザー名とパスを決めます。後は127.0.0.1:9000で管理画面へ入ります。成功すると次のような画面になります。



有効後は下記より色々情報を得られます

ちょっと疲れたので休憩

と、いろいろ分かってスッキリしたのと、テストを何度か繰り返して、不具合?AIをオフにしているのにも関わらずAIが参加してきたりまだテストが足りない感じ。これは公式フォーラムでも報告されていました。バグじゃねーか!?って騒がれています。

もし鯖建てで困っている人の役に立てば幸いです。


関連記事


“Project Cars 2 – Dedicated Serverの建て方はクセがある” への5件のフィードバック

  1. shiny より:

    情報助かります。
    ローテーションはオフにしてますが、dedi鯖を立ち上げブラウザから管理画面で設定できることは確認しました。ただクライアントソフト内の鯖リストに現れません。何度も再表示しても。以下には現れるので外から見えていると思うのですが…。何が足らないんでしょうね?dedi鯖PCと同一PCからは見えないのですかね?
    http://cars2-stats-steam.wmdportal.com/index.php/servers
    yo-tarouさんの鯖はリストで見かけたので構成など教えていただけると幸いです。

    • Yotarou より:

      コメントありがとうございます!

      まず、自分の場合は同一LANにある別のWinPCで鯖を起動しています。同一PCで鯖とクライアントを起動するケースは経験がないので確かな情報はありませんが、次の項目を確認してみてください。

      ・Dedi鯖フォルダ内にserver.cfgを置いているか確認
      ・Port 8766, 27015, 27016 をUDP,TCP共に開放(server.cfg 32行目と同期)
      ・ipアドレスを固定
      ・DedicatedServerCmd.exe で起動後表示されるコマンドプロンプト(黒いウィンドウ)内にERROR項目がないか確認する。あれば修正。

      後は上のserver.cfgを丸ごとコピーして部屋設定を変えれば動かないでしょうか。ローテを使用しないのであればenableLuaApi : falseです。

      それでもだめなら、前作の情報ですが・・
      SteamのpCars2の起動設定に -searchds “yourservername” を入れて起動してみたらどうでしょう?※もし他の人からは見えているのであれば・・

      今はpCars2のdedi鯖が全部0人だしAIの問題だったり調子がよくないみたいですね。あと、自分だけかもしれませんがdedi鯖一覧を表示させた後、メニューに戻り、再度一覧表示させた時は必ず部屋数が0になります。

      その他、思い浮かぶとしたら・・Dedi鯖ファイルをSteamから落とすのではなく、SteamCMDから落としそちらで起動するか・・。私もプログラミングは素人なので正確な答えは分かりませんが、無事に起動すると良いですね!

  2. shiny より:

    詳細にありがとうございます。
    今日は事情により試せそうにありませんが、近く参考にして再トライしてみます。

    • Yotarou より:

      自分のサーバーも建ってはいますが、他の人が入れるのかが分かってません。昨日も何度かテストしましたがAIは相変わらず登場しますね。

      AIに関する項目はFlagsのFILL_SESSION_WITH_AIを入れないのとGridSizeとMaxPlayersが関係していると思うのですがよく分かってません。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です